Java 模式匹配的有效方法

Efficicient way for Java pattern Matching

检查字符串是否匹配以下内容的最佳和最快方法是什么:"AB+SPACE+NUMBER+ANYTHING 例如:"AB 1234 DEFG" 我正在寻找最有效的方法,因为它将每分钟比较数千笔交易。

使用 string.matches 函数,该函数使用正则表达式进行字符串匹配。

string.matches("AB \d+.*");
                ^ ^  ^ ^
                | |  | |_ Anything
               AB | Num
                  |
                space

如果你想在数字后面加上 space,那么使用

"AB \d+ .+"

注意:我真的很不擅长编写高效的代码,除非我使用 scala,否则情况会有所不同,但现在我的 scala IDE 无法正常工作,所以你只能得到 java。
Java:(同样,可能效率不高)

public static boolean checkString(String original) {
    int space1 = original.indexOf(" ");
    String section1 = original.substring(0, space1);
    String sections2Onwards = original.substring(space1+1, original.length());
    int space2 = sections2Onwards.indexOf(" ");
    String section2 = sections2Onwards.substring(0,space2);
    String end = original.substring(space2+1, original.length());
    //Now on to the good, fun part, making sure that it is in fact the right pattern
    //This checks that every character in section1 is not a number
    char[] section1Split = section1.toCharArray();
    for (char c : section1Split) {
        try {
            new Integer(new String(new char[] {c}));
            return false;
        } catch(NumberFormatException n) {}
    }
    //Now check that section2 is a number
    try {
        new Integer(section2);
    } catch(NumberFormatException n) {
        return false;
    }
    //Making sure that there are no spaces in "anything"
    //Ignore this if "anything" can include spaces
    if(end.indexOf(" ") > -1) {
        return false;
    }
    //Since all conditions are true, return true! This string is legit!
    return true;
}

Avinash 的回答可能更好更快,但我(以及堆栈溢出的其他很多人)无法理解如何制作这些模式。

希望我有所帮助!

避免使用 yourString.matches(regex),因为每次使用 matches 时都需要重新编译正则表达式,这可能是一项昂贵的操作。
最好编译一次正则表达式并在需要时重用它,如

Pattern p = Pattern.compile(regex);//compile once
for (String data : dataCollection){
    Matcher m = p.matcher(data);
    //... if (m.foo(bar))//some condition

}

避免 matches 的另一个原因是它可以 return 为真,如果整个数据都匹配正则表达式,这意味着它需要遍历整个字符串,即使您可以立即做出决定在数据开头看到 AB 1234 部分后。

因此,与其检查字符串是否为 AB(space)digits(restOfData) 形式,这需要我们遍历 restOfData 部分,我们可以检查是否可以 find AB(space)digits 部分 在字符串的开头。这样的正则表达式可能看起来像 ^AB\s\d+

  • ^ - 表示字符串开头的锚点
  • AB - 表示 AB 文本的文字
  • \s - 空白字符之一
  • \d+ - 数字 ("\d") 可以出现一次或多次 (+)

所以你的代码看起来像

private static final Pattern p = Pattern.compile("^AB\s\d+");
private static boolean validate(String data){
    return p.matcher(data).find();
}

要提取最大吞吐量,您也许可以使用状态机击败预编译的正则表达式 Pattern - 只要您可以对模式进行编码并且它不会改变。

enum Match {

    Start {

                @Override
                Match match(char c) {
                    return A.match(c);
                }

            },
    A {

                @Override
                Match match(char c) {
                    return c == 'A' ? B : Fail;
                }

            },
    B {

                @Override
                Match match(char c) {
                    return c == 'B' ? Space : Fail;
                }

            },
    Space {

                @Override
                Match match(char c) {
                    return c == ' ' ? Number : Fail;
                }

            },
    Number {

                @Override
                Match match(char c) {
                    return Character.isDigit(c) ? Number : Anything;
                }

            },
    Anything {

                @Override
                Match match(char c) {
                    return Anything;
                }

            },
    Fail {

                @Override
                Match match(char c) {
                    return Fail;
                }

            };

    abstract Match match(char c);

    static boolean matches(String s) {
        Match state = Start;
        for (int i = 0; i < s.length() && state != Fail; i++) {
            state = state.match(s.charAt(i));
        }
        return state != Fail;
    }
}

public void test() {
    List<String> tests = Arrays.asList("AB 123Hello", "ABC 123Hello", "AB Hello", "AB 0 Hello");
    for (String s : tests) {
        boolean match = Match.matches(s);
        System.out.println(s + " - " + (match ? "Matches" : "Fails"));

    }
}

实际上,可以动态构建这些状态机 - 这就是 Pattern.compile 所做的 - 但像这样发展自己的硬编码 有时 实现更快的吞吐量。

请务必在使用前根据标准 Java Pattern 测试您的状态机,因为很容易发生 Pattern 实际上更快一点的情况。

请注意,此处编码的数字可以为零。如果这是可以接受的,那么您可以完全跳过该状态,直接从 Space 转到 Anything。如果不是,则添加 FirstDigit 状态以确保至少存在一位数字。您甚至可以在 FirstDigit.

之后直接转到 Anything