我有以下代码,它有效,但我想知道是否有一种“更加常规”的方法:
/**
* 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
}
}
答案 0 :(得分: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]
我会留给你弄清楚它是如何工作的,但我想将它留在生产代码上可能不是一个好主意,除非你想让未来的维护者感到惊讶。
另外,请注意length == 13
与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'
现在,我认为该代码中有一些难闻的气味。 isbn
可以null
吗?至少对我来说,这看起来不像是一个需要打扰其论证的无效性的函数,或者至少通过阅读它的名称并不清楚(如果两个ISBN都应该被称为类似formatIsbnOrNull
的函数接受字符串和空值)。如果null值无效,那么在访问NullPointerException
时让它以isbn.length()
爆炸,以便调用者知道他们传递了错误的参数,而不是静默返回相同的null。
最后return ISBN
也是如此。是否希望该函数接收一个既不是10个也不是13个字符的字符串?如果不是,那就更好throw new IllegalArgumentException()
并让来电者知道他们错误地调用了它。
最后,我不确定这是否是最“可读”的解决方案。另一种可能的解决方案是使用格式的字符串,例如'###-#-######-##-#'
,然后用#
字符替换isbn
。我认为它可能更具自我记录性:
def formatIsbn(isbn) {
def format = [
10: '#-######-##-#',
13: '###-#-######-##-#'
][isbn.length()]
def n = 0
format.replaceAll(/#/) { isbn[n++] }
}
答案 1 :(得分:4)
考虑将方法添加到String类,如下所示。请注意,这个答案是关于流行病学家的答案(re:collect)的一个聪明建议。
注意:
此代码使用asIsbn
()扩充String。
范围[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
答案 2 :(得分:3)
我会尝试使用Regex
...我认为如果您知道如何使用正则表达式,它几乎可读,而且它在groovy中的javascript启发语法也非常酷。
还有一件事:非常清楚,看看捕获组,你的字符串对于所需的格式是什么样的。
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
}
}
答案 3 :(得分: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("-")
}
使用两个注入式累加器,也很容易做一个破折号位置版本。
答案 4 :(得分: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