检查`c> ='0'`或`c> = 48`更好吗?


46

与我的一些同事讨论之后,我遇到了一个“哲学上的”问题,即如何按照最佳实践来对待Java中的char数据类型。

假设有一个简单的场景(显然,这只是一个非常简单的示例,目的是为我的问题赋予实践意义),在给定String's'作为输入的情况下,您必须计算其中存在的数字字符的数量。

这些是2种可能的解决方案:

1)

    for(int i=0; i<s.length(); i++) {
        if(s.charAt(i) >= 48 && s.charAt(i) <= 57) {
            n++;
        }
    }

2)

    for(int i=0; i<s.length(); i++) {
        if(s.charAt(i) >= '0' && s.charAt(i) <= '9' ) {
            n++;
        }
    }

两者中哪一个更“干净”并符合Java最佳实践?


141
当您实际上表示“ 0”和“ 9”时,为什么还要写48和57?只需写下您的意思。
布兰丁2015年

9
等待您在做什么,Java具有VK_您应该使用的常量,其次,使用char代码比char要好。Java是一种类型安全的语言,您不应该执行跨类型检查。@Brandin所谓的编码实践
Martin Barker

12
不必费心去判断6个人,这是一个很好的问题。您使用字符作为数字吗?如果是这样,请使用数字。您是否将其用作字母?如果是这样,请使用字母。
亚历克·蒂尔

17
@MartinBarker VK_*常量对应于而不是字符
CodesInChaos

2
我花了几分钟时间来确定该代码与您的问题有关。目前尚不清楚,因为它假设我在(1)中知道我知道这是ISO-Latin 1的数字范围。因此,从维护的角度来看,这是有问题的。
Cyber​​Skull

Answers:


124

两者都很可怕,但第一个更可怕。

两者都忽略了Java的内置功能来确定哪些字符是“数字”(通过中的方法Character)。但是第一个不但忽略了字符串的Unicode本质(假设只能有0123456789),而且使用仅当您了解字符编码历史的情况下才有意义的字符代码,甚至掩盖了这种无效的推理。


33
为什么要假设非拒绝非ASCII数字是错误的?这取决于上下文。
CodesInChaos

21
@CodesInChaos如果您确实要查找数字字符,则扫描0123456789是完全错误的。如果您确实只想扫描这十个字符,则它们本质上是无意义的标记,对于仅懂ASCII / ISO-Latin的人来说,它们只是偶然而已。这没什么不对-我经常必须精确地做到这一点,例如与实际上只接受这十个字符的旧版软件进行交互。但是,然后您应该使用来表明您的意图matches("[0-9]+"),而不是利用历史动机的范围技巧。
Kilian Foth,2015年

15
全角数字,看起来与ASCII数字相同,并且通常需要很多软件来代替ASCII数字。(显然,根据“很多”的定义,很多软件都已损坏。您可以很容易地说出来,因为一个国家/地区的软件供应商发现不可能出售给另一个国家/地区,因为这些供应商不遵守其他国家/地区的要求。 )
rwong

37
I hapanes I ME Installed,accientally type i full-width all th time。
BlueRaja-Danny Pflughoeft 2015年

14
“两者都很可怕”,但是您忘了说正确的解决方案;-)
Kromster说支持Monica

163

都不行 让Java的内置Character类为您解决。

for (int i = 0; i < s.length(); ++i) {
  if (Character.isDigit(s.charAt(i))) {
    ++n;
  }
}

字符范围比算作数字的ASCII数字要多,并且您发布的任何示例都不会对其进行计数。该JavaDoc中Character.isDigit()列出了这些字符范围为有效位数:

某些包含数字的Unicode字符范围:

  • 从'\ u0030'到'\ u0039',ISO-LATIN-1数字(从'0'到'9')
  • '\ u0660'至'\ u0669',阿拉伯文-印度数字
  • '\ u06F0'到'\ u06F9',扩展的阿拉伯文-印度数字
  • '\ u0966'至'\ u096F',梵文数字
  • '\ uFF10'至'\ uFF19',全角数字

许多其他字符范围也包含数字。

话虽这么说,但Character.isDigit()即使有这个清单,也应该委托给。随着新Unicode平面的填充,Java代码将被更新。升级JVM可以使旧代码与新数字字符无缝兼容。它也是DRY:通过将“这是一个数字”代码本地化到其他地方引用的位置,可以避免代码重复的负面影响(即错误)。最后,请注意最后一行:此列表并不详尽,还有其他数字。

就个人而言,我宁愿委托给Java核心库,而不是花一些时间来完成更有成效的任务。


该规则的唯一例外是,如果您确实确实需要测试文字ASCII数字而不是其他数字。例如,如果您正在解析流,并且只有 ASCII数字(与其他数字相对)具有特殊含义,则不适合使用Character.isDigit()

在这种情况下,我将编写另一种方法,例如MyClass.isAsciiDigit(),然后将逻辑放入其中。您可以获得代码重用的相同好处,名称在检查内容方面非常清楚,并且逻辑正确。


4
实际提供完成此操作的干净代码的好答案。
皮埃尔·阿洛德

27

如果您曾经用C编写使用EBCDIC作为基本字符集的应用程序,并且需要处理ASCII字符,请使用4857。你在那样吗 我不这么认为。

关于使用isDigit():取决于。您正在编写JSON解析器吗?仅0将to 9接受为数字,因此请勿使用isDigit(),检查>= '0'<= '9'。您正在处理用户输入吗?使用isDigit()只要你的代码的其余部分实际上可以处理字符串,并正确地把它变成一个数字。


3
实际上,您可以使用Java编写应用程序,以获取和返回EBCDIC。不好玩
托尔比约恩Ravn的安徒生

类似的“不好玩”正在遍历使用EBCDIC字符的十进制值编写的代码,并将其转换为跨平台环境时使用的代码
Gwyn Evans 2015年

1
如果要用Java处理EBCDIC数据,那么在将其作为字符处理之前,可能应该将其转换为Java本机UTF-16字符集。但是我想这真的取决于应用程序。希望如果您的程序必须处理EBCDIC,那么您将了解需要做什么。
Michael Burr

1
主要的一点是,在Java处理EBCDIC两个“0”和48是错误的,以检测一个数字零。在C,C ++等环境中,当前版本更多。“ \ n”和“ \ r”已实现定义,因此,如果要使用非Windows编译器检测文件中的Windows CR / LF对,最好检查十进制值而不是检查“ \ n”和“ \ r”。
gnasher729,2015年

12

第二个例子显然是优越的。当您查看代码时,第二个示例的含义立即显而易见。仅当您将整个ASCII表存储在头脑中时,第一个示例的含义才显而易见。

您应该区分检查特定字符还是检查字符范围或类别。

1)检查特定字符。

对于普通字符,请使用字符文字,例如if(ch=='z')...。如果您对制表符或换行符之类的特殊字符进行检查,则应使用转义符,如if (ch=='\n')...。如果要检查的字符不正常(例如,无法立即识别或在标准键盘上不可用),则可以使用十六进制字符代码,而不是文字字符。但是,由于十六进制代码是“魔术值”,因此您可以将其提取为常量并将其记录下来:

const char snowman = 0x2603; // snowman char used to detect encoding issues
...
if (ch==showman)...

十六进制代码是指定字符代码的标准方法。

2)检查字符类别或范围

您实际上不应该直接在应用程序代码中执行此操作,而应将其封装在仅与字符分类有关的单独类中。而且您应该对此有所不同,因为为此已经存在库,并且字符分类通常比您想象的要复杂,至少如果考虑的字符不在ASCII范围内。

如果只关心ASCII范围内的字符,则可以在此库中使用字符文字,否则可能会使用十六进制字面量。如果您查看Java内置字符库的源代码,它也会使用十六进制引用字符值和范围,因为这是在Unicode标准中指定的方式。


1
我还建议'\x2603'您使用十六进制写字符文字,以明确表示您正在测试使用十六进制编码的字符的值,而不仅仅是任何随机数。
wefwefa3

-4

总是最好使用它,c >= '0'因为c >= 48您需要将c转换为ascii代码。


3
这个答案说明一个星期前在以前的答案中没有说过什么?

-5

正则表达式RegEx)具有数字的特定字符类\d --可用于从字符串中删除任何其他字符。结果字符串的长度是所需的值。

public static int countDigits(String str) {
    str = Objects.requireNonNull(str).trim();

    return str.replaceAll("[^\\d]", "").length();
}

但是请注意,RegEx在计算上比其他提出的解决方案要求更高,因此通常不应首选RegEx


做检查的方式非常优雅!
凯文·罗巴特尔

正则表达式对于这样的任务
过于杀伤

2
@StefanoBragaglia重新阅读您的答案后,我认为它并没有真正回答问题。
法拉普2015年

2
您的答案提供了解决“如何计数字符串中的数字”问题的另一种方法。它不能用代码示例和常量的表示形式(数字或字符)来回答潜在的问题。

2
这实际上并没有计算数字(它只是告诉您删除所有数字后字符串的长度,该数字既不存在也不存在),但是我同意它实际上并没有回答问题。例如,没有人问过要从字符串中删除字符。问题只是询问用于检查字符是否为数字的最佳实践方法。
doppelgreener
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.