【问题标题】:Java - what is the best way to check if a STRING contains only certain characters?Java - 检查字符串是否仅包含某些字符的最佳方法是什么?
【发布时间】:2014-12-20 17:31:12
【问题描述】:

我有这个问题:我有一个String,但我需要确保它包含字母AZ和数字0-9。这是我当前的代码:

boolean valid = true;
for (char c : string.toCharArray()) {
    int type = Character.getType(c);
    if (type == 2 || type == 1 || type == 9) {
        // the character is either a letter or a digit
    } else {
        valid = false;
        break;
    }
}

但是最好和最有效的实现方式是什么?

【问题讨论】:

  • 你熟悉regexes这个概念吗?
  • 使用正则表达式
  • 如果您不想使用regex,请使用Character 包装类的预定义函数。转换您的字符串 toCharArray 然后使用循环并使用预定义的方法
  • 你真的想要0-9,还是想要任何数字,甚至是this one
  • 我相信 OpenCL 在这方面会有很大帮助:D

标签: java regex string char


【解决方案1】:

由于还没有人担心“最快”,这是我的贡献:

boolean valid = true;

char[] a = s.toCharArray();

for (char c: a)
{
    valid = ((c >= 'a') && (c <= 'z')) || 
            ((c >= 'A') && (c <= 'Z')) || 
            ((c >= '0') && (c <= '9'));

    if (!valid)
    {
        break;
    }
}

return valid;

完整的测试代码如下:

public static void main(String[] args)
{
    String[] testStrings = {"abcdefghijklmnopqrstuvwxyz0123456789", "", "00000", "abcdefghijklmnopqrstuvwxyz0123456789&", "1", "q", "test123", "(#*$))&v", "ABC123", "hello", "supercalifragilisticexpialidocious"};

    long startNanos = System.nanoTime();

    for (String testString: testStrings)
    {
        isAlphaNumericOriginal(testString);
    }

    System.out.println("Time for isAlphaNumericOriginal: " + (System.nanoTime() - startNanos) + " ns"); 

    startNanos = System.nanoTime();

    for (String testString: testStrings)
    {
        isAlphaNumericFast(testString);
    }

    System.out.println("Time for isAlphaNumericFast: " + (System.nanoTime() - startNanos) + " ns");

    startNanos = System.nanoTime();

    for (String testString: testStrings)
    {
        isAlphaNumericRegEx(testString);
    }

    System.out.println("Time for isAlphaNumericRegEx: " + (System.nanoTime() - startNanos) + " ns");

    startNanos = System.nanoTime();

    for (String testString: testStrings)
    {
        isAlphaNumericIsLetterOrDigit(testString);
    }

    System.out.println("Time for isAlphaNumericIsLetterOrDigit: " + (System.nanoTime() - startNanos) + " ns");      
}

private static boolean isAlphaNumericOriginal(String s)
{
    boolean valid = true;
    for (char c : s.toCharArray()) 
    {
        int type = Character.getType(c);
        if (type == 2 || type == 1 || type == 9) 
        {
            // the character is either a letter or a digit
        }
        else 
        {
            valid = false;
            break;
        }
    }

    return valid;
}

private static boolean isAlphaNumericFast(String s)
{
    boolean valid = true;

    char[] a = s.toCharArray();

    for (char c: a)
    {
        valid = ((c >= 'a') && (c <= 'z')) || 
                ((c >= 'A') && (c <= 'Z')) || 
                ((c >= '0') && (c <= '9'));

        if (!valid)
        {
            break;
        }
    }

    return valid;
}

private static boolean isAlphaNumericRegEx(String s)
{
    return Pattern.matches("[\\dA-Za-z]+", s);
}

private static boolean isAlphaNumericIsLetterOrDigit(String s)
{
    boolean valid = true;
    for (char c : s.toCharArray()) { 
        if(!Character.isLetterOrDigit(c))
        {
            valid = false;
            break;
        }
    }
    return valid;
}

为我生成这个输出:

Time for isAlphaNumericOriginal: 164960 ns
Time for isAlphaNumericFast: 18472 ns
Time for isAlphaNumericRegEx: 1978230 ns
Time for isAlphaNumericIsLetterOrDigit: 110315 ns

【讨论】:

  • +1。我喜欢这个解决方案。它与我的解决方案相比如何?
  • @Cyber​​neticTwerkGuruOrc:我已经更新了我的答案以包含您的解决方案。看起来你的比我的慢大约 6 倍,模式匹配解决方案慢 107 倍,原始解决方案大约慢 9 倍。我可以用正则表达式解决方案做的一件事是预编译模式。我已经尝试过了,它确实加快了一些速度,但最终还是慢了 40 倍。
  • 这是我见过的最直接的解决方案,谢谢!
【解决方案2】:

如果您想避免使用正则表达式,那么 Character 类可以提供帮助:

boolean valid = true;
for (char c : string.toCharArray()) { 
    if(!Character.isLetterOrDigit(c))
    {
        valid = false;
        break;
    }
}

如果您关心大写,请改为使用以下 if 语句:

if(!((Character.isLetter(c) && Character.isUpperCase(c)) || Character.isDigit(c)))

【讨论】:

  • 这将接受小写字母和可能来自其他字符集的字母/数字字符(任何被认为是 Unicode \u0000 - \uFFFF 范围内的字母或数字)。
【解决方案3】:

您可以使用 Apache Commons Lang:

StringUtils.isAlphanumeric(String)

【讨论】:

  • 是的。 StringUtils 是你的朋友。
【解决方案4】:

除了所有其他答案,这里是番石榴方法:

boolean valid = CharMatcher.JAVA_LETTER_OR_DIGIT.matchesAllOf(string);

有关 CharMatcher 的更多信息:https://code.google.com/p/guava-libraries/wiki/StringsExplained#CharMatcher

【讨论】:

    【解决方案5】:

    Apache Commons Lang 3 中的 StringUtils 有一个 containsOnly 方法,https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html

    实现应该足够快。

    【讨论】:

      【解决方案6】:

      使用regular expression

      Pattern.matches("[\\dA-Z]+", string)
      

      [\\dA-Z]+:至少出现一次 (+) 数字或大写字母。

      如果要包含小写字母,请将[\\dA-Z]+ 替换为[\\dA-Za-z]+

      【讨论】:

      • 仍然返回 'true' 和 "!username"
      • 这会检查它是否包含至少一个数字或大写字母,而不是它只包含数字和大写字母。
      • 需要指定字符串^$的开始和结束才能只获取数字/字母
      • 但是如何检查字符串是否仅由数字和字母组成?
      • 可以说是简洁优雅的最佳解决方案。就“最快”而言,可以说是最差的。
      【解决方案7】:

      以下方法的实现速度不如正则表达式,但它是最有效的解决方案之一(我认为),因为它使用非常快的按位运算。

      我的解决方案更复杂,更难阅读和维护,但我认为这是另一种简单的方法来做你想做的事。

      测试字符串是否仅包含数字或大写字母的好方法是使用简单的128 bits bitmask(2 个长整数)表示 ASCII 表。

      因此,对于标准 ASCII 表,我们要保留的每个字符都有一个 1(第 48 位到第 57 位和第 65 位到 90 位)

      因此,您可以测试 char 是否为:

      1. 带有此掩码的数字:0x3FF000000000000L(如果字符代码
      2. 带有此掩码的大写字母:0x3FFFFFFL(如果字符代码 >=65)

      所以下面的方法应该可以工作:

      public boolean validate(String aString) {
          for (int i = 0; i < aString.length(); i++) {
              char c = aString.charAt(i);
      
              if ((c <= 64) & ((0x3FF000000000000L & (1L << c)) == 0) 
                      | (c > 64) & ((0x3FFFFFFL & (1L << (c - 65))) == 0)) {
                  return false;
              }
          }
      
          return true;
      }
      

      【讨论】:

      • 不错。我唯一能提供的是这个解决方案只处理大写字符。
      • 是的,因为它是在问题中指定的:“我需要确保它只包含字母 A-Z”。
      • 很公平。顺便说一句,我将您的解决方案与我在上面的答案中所做的其他解决方案进行了比较,它确实占据了头把交椅。我的解决方案为 18659 ns,您的解决方案为 15861 ns。恭喜。
      【解决方案8】:

      在可维护性和简单性方面最好的方法是已经发布的正则表达式。一旦熟悉了这项技术,您就会知道会发生什么,并且如果需要,很容易扩大标准。缺点是性能。

      最快的方法是数组方法。检查一个字符的数值是否在想要的范围内 ASCII A-Z 和 0-9 几乎是光速。但是可维护性很差。简单性消失了。

      您可以使用 char 方法和 java 7 switch case,但这和第二种方法一样糟糕。

      最后,既然我们在谈论java,我强烈建议使用正则表达式。

      【讨论】:

      • 我同意你的看法。在大多数情况下,正则表达式是最易读和最简单的方法。但是,如果您关心实时性能,那么字符串模式匹配恰好是真正让您头疼的事情之一。在这种情况下,每一毫秒都可以计算在内,使用正则表达式编译的模式可能会对性能产生真正的影响。
      猜你喜欢
      • 2014-05-07
      • 2015-03-15
      • 2016-08-09
      • 2012-06-15
      • 1970-01-01
      • 2018-05-01
      • 2011-03-31
      • 1970-01-01
      相关资源
      最近更新 更多