8

我有以下代码,它有效,但我想知道是否有一种“groovier”的方式来做到这一点:

/**
   * 10 digit - #-######-##-#
   * 13 digit - ###-#-######-##-#
   * */
private formatISBN(String isbn) {
  if (isbn?.length() == 10) {
    def part1 = isbn.substring(0, 1)
    def part2 = isbn.substring(1, 7)
    def part3 = isbn.substring(7, 9)
    def part4 = isbn.substring(9, 10)
    return "${part1}-${part2}-${part3}-${part4}"
  } else if (isbn?.length() == 13) {
    def part1 = isbn.substring(0, 3)
    def part2 = isbn.substring(3, 4)
    def part3 = isbn.substring(4, 10)
    def part4 = isbn.substring(10, 12)
    def part5 = isbn.substring(12, 13)
    return "${part1}-${part2}-${part3}-${part4}-${part5}"
  } else {
    return isbn
  }
}
4

5 回答 5

8

您可以首先使用[]字符串运算符来获取子字符串,而不是substring删除中间变量。例如在以下情况下length == 10

"${isbn[0]}-${isbn[1..6]}-${isbn[7..8]}-${isbn[9]}"

现在,那里有一些重复。相反,您可以先获取所有isbn细分,然后再.join使用'-'

[isbn[0], isbn[1..6], isbn[7..8], isbn[9]].join('-')

而且,更进一步,isbn您可以列出您想要获取的范围,然后使用以下命令同时获取它们,而不是每次都引用collect

[0, 1..6, 7..8, 9].collect { isbn[it] }.join('-')

如果您要进行代码打高尔夫球,您还可以执行以下操作:

('-'+isbn)[1, 0, 2..7, 0, 8..9, 0, 10]

我会让你弄清楚它是如何工作的,但我想把它留在生产代码上可能不是一个好主意,除非你想给未来的维护者一个惊喜,呵呵。


此外,请注意格式 whenlength == 13与 for 相同length == 10但具有不同的前缀,然后您可以在这种情况下重用相同的函数。整个功能(通过几个测试)将是:

/**
 * 10 digit - #-######-##-#
 * 13 digit - ###-#-######-##-#
 **/
def formatIsbn(isbn) {
    switch (isbn?.length()) {
        case 10: return [0, 1..6, 7..8, 9].collect { isbn[it] }.join('-')
        case 13: return isbn.take(3) + '-' + formatIsbn(isbn.drop(3))
        default: return isbn
    }
}

assert formatIsbn('abcdefghij') == 'a-bcdefg-hi-j'
assert formatIsbn('abcdefghijklm') == 'abc-d-efghij-kl-m'

现在,我认为该代码中有一些难闻的气味。可以isbnnull?至少对我来说,这看起来不像是一个需要担心其参数的无效性的函数,或者至少通过阅读它的名称并不清楚(formatIsbnOrNull如果 ISBN 字符串和 null 值都是公认)。NullPointerException如果 null 值无效,则在访问时让它爆炸,isbn.length()以便调用者知道他们传递了错误的参数,而不是默默地返回相同的 null。

最后也是一样return ISBN。该函数是否期望接收一个既不是 10 也不是 13 个字符长的字符串?如果没有,最好throw new IllegalArgumentException()让来电者知道他们打错了电话。


最后,我不确定这是否是最“可读”的解决方案。另一种可能的解决方案是为格式设置一个字符串,例如'###-#-######-##-#'然后将#s 替换为isbn字符。我认为它可能更自我记录:

def formatIsbn(isbn) {
    def format = [
        10: '#-######-##-#',
        13: '###-#-######-##-#'
    ][isbn.length()]
    def n = 0
    format.replaceAll(/#/) { isbn[n++] }
}
于 2012-07-06T02:30:26.393 回答
4

考虑将方法添加到 String 类,如此处所示。请注意,这个答案是对流行病答案(re:collect)中一个聪明建议的转折。

笔记:

asIsbn此代码用()扩充字符串。

范围[0..2]不需要调用asIsbn(),但是使用collect两次的对称性是不可抗拒的。

Groovy 返回 中的最后一个表达式if/else,因此不需要“return”

/**
 * 10 digit - #-######-##-#
 * 13 digit - ###-#-######-##-#
 **/
String.metaClass.asIsbn = { ->
    if (delegate.length() == 10) {
        [0, 1..6, 7..8, 9].collect { delegate[it] }.join('-')
    } else if (delegate.length() == 13) {
        [0..2, 3..12].collect { delegate[it].asIsbn() }.join('-')
    } else {
        delegate
    }
}

assert "abcdefghij".asIsbn() == 'a-bcdefg-hi-j'
assert "abcdefghijklm".asIsbn() == 'abc-d-efghij-kl-m'
assert "def".asIsbn() == "def"
String s = null 
assert s?.asIsbn() == null
于 2012-07-06T03:30:11.577 回答
3

我会尝试使用Regex...

还有一件事:很清楚,查看捕获组,您的字符串对于所需格式的外观。

private formatISBN(String isbn) {
    if (isbn?.length() == 10) {
        m = isbn =~ /(\d{1})(\d{6})(\d{2})(\d{1})/
        return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}"
    } else if (isbn?.length() == 13) {
        m = isbn =~ /(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/
        return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}-${m[0][5]}"        
    } else {
        return isbn
    }
}

顺便说一句,@epidemian 建议使用反向引用很棒!我认为代码看起来像:

private formatISBN(String isbn) {
    if (isbn?.length() == 10) {
        return isbn.replaceAll(/(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4')
    } else if (isbn?.length() == 13) {
        return isbn.replaceAll(/(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4-$5')
    } else {
        return isbn
    }
}
于 2012-07-06T02:34:25.523 回答
2

不知道我是否更喜欢这个。我也会将位置图设为静态决赛。

private isbnify(String isbn) {
  def dashesAt = [ 10: [[0,1], [1,7], [7,9],  [9,10]],
                   13: [[0,3], [3,4], [4,10], [10,12], [12,13]]]
  def dashes = dashesAt[isbn?.length()]
  (dashes == null) ? isbn 
                   : dashes.collect { isbn.substring(*it) }.join('-')
}

IMO,范围可以减少混乱:

private isbnify3(String isbn) {
  def dashesAt = [ 10: [0, 1..6, 7..8, 9],
                   13: [0..2, 3, 4..9, 10..11, 12]]
  def dashes = dashesAt[isbn?.length()]
  dashes == null ? isbn : dashes.collect { isbn[it] }.join("-")
}

使用带有两个累加器的注入,也应该很容易做一个破折号位置列表版本。

于 2012-07-06T01:32:41.837 回答
0

这应该是对@everton 的评论,但我还没有达到所需的 50 名声望。所以这个答案实际上只是@everton 答案的一个建议变体。

通过将前 3 位数字设为可选,减少一个正则表达式。缺点是如果 ISBN 为 10 个字符,则必须删除前导“-”。(我也更喜欢\d\d{1}

private formatISBN(String isbn) {
  String result = isbn.replaceAll(/^(\d{3})?(\d)(\d{6})(\d{2})(\d)$/,
                                  '$1-$2-$3-$4-$5')
  if (result) {
    return result.startsWith('-') ? result[1..-1] : result
  } else {
    return isbn // return value unchanged, pattern didn't match
  }
}

println formatISBN('1234567890')
println formatISBN('9991234567890')
println formatISBN('123456789') // test an ISBN that's too short
println formatISBN('12345678901234') // test an ISBN that's too long
于 2019-11-21T19:27:46.553 回答