解析一行数据:split vs regex

时间:2014-03-13 15:55:42

标签: java regex performance parsing split

我有来自脚本的数据行,通常看起来像这样(单行示例):

1234567890;group1;varname1;133333337;prop1=val1;prop2=val2;prop3=val3

我需要将每一行分成一个Map的Key-Value项,每个项用一个分隔符字符串(;在示例中分隔,但它也可以是自定义的)。前4个项是静态的,这意味着只有值在行中,并且键已经知道。其余的是可变数量的键值项(0个或更多key=value个块)。 请先查看下面的输出,以便给出一个想法。

我已经有两种工作方法可以实现这一点,两者都为同一行输出相同的输出。我已经建立了一个测试类来演示两种方法,以及一些(简单的)性能分析,仅仅是出于好奇。 请注意,在下面显示的方法中,无效的输入处理最少。

String Splitting(使用Apache Commons):

private static List<String> splitParsing(String dataLine, String separator) {
    List<String> output = new ArrayList<String>();
    long begin = System.nanoTime();

    String[] data = StringUtils.split(dataLine, separator);

    if (data.length >= STATIC_PROPERTIES.length) {
        // Static properties (always there).
        for (int i = 0; i < STATIC_PROPERTIES.length; i++) {
            output.add(STATIC_PROPERTIES[i] + " = " + data[i]);
        }

        // Dynamic properties (0 or more).
        for (int i = STATIC_PROPERTIES.length; i < data.length; i++) {
            String[] fragments = StringUtils.split(data[i], KEYVALUE_SEPARATOR);
            if (fragments.length == 2) {
                output.add(fragments[0] + " = " + fragments[1]);
            }
        }
    }

    long end = System.nanoTime();
    output.add("Execution time: " + (end - begin) + "ns");
    return output;
}

正则表达式(使用JDK 1.6):

private static List<String> regexParsing(String dataLine, String separator) {
    List<String> output = new ArrayList<String>();
    long begin = System.nanoTime();

    Pattern linePattern = Pattern.compile(StringUtils.replace(DATA_PATTERN_TEMPLATE, SEP, separator));
    Pattern propertiesPattern = Pattern.compile(StringUtils.replace(PROPERTIES_PATTERN_TEMPLATE, SEP, separator));

    Matcher lineMatcher = linePattern.matcher(dataLine);
    if (lineMatcher.matches()) {
        // Static properties (always there).
        for (int i = 0; i < STATIC_PROPERTIES.length; i++) {
            output.add(STATIC_PROPERTIES[i] + " = " + lineMatcher.group(i + 1));
        }

        Matcher propertiesMatcher = propertiesPattern.matcher(lineMatcher.group(STATIC_PROPERTIES.length + 1));
        while (propertiesMatcher.find()) {
            output.add(propertiesMatcher.group(1) + " = " + propertiesMatcher.group(2));
        }
    }

    long end = System.nanoTime();
    output.add("Execution time: " + (end - begin) + "ns");
    return output;
}

主要方法:

public static void main(String[] args) {
    String input = "1234567890;group1;varname1;133333337;prop1=val1;prop2=val2;prop3=val3";

    System.out.println("Split parsing:");
    for (String line : splitParsing(input, ";")) {
        System.out.println(line);
    }

    System.out.println();

    System.out.println("Regex parsing:");
    for (String line : regexParsing(input, ";")) {
        System.out.println(line);
    }
}

常量:

// Common constants.
private static final String TIMESTAMP_KEY = "TMST";
private static final String GROUP_KEY = "GROUP";
private static final String VARIABLE_KEY = "VARIABLE";
private static final String VALUE_KEY = "VALUE";
private static final String KEYVALUE_SEPARATOR = "=";
private static final String[] STATIC_PROPERTIES = { TIMESTAMP_KEY, GROUP_KEY, VARIABLE_KEY, VALUE_KEY };

// Regex constants.
private static final String SEP = "{sep}";
private static final String PROPERTIES_PATTERN_TEMPLATE = SEP + "(\\w+)" + KEYVALUE_SEPARATOR + "(\\w+)";
private static final String DATA_PATTERN_TEMPLATE = "(\\d+)" + SEP + "(\\w+)" + SEP + "(\\w+)" + SEP + "(\\d+\\.?\\d*)"
        + "((?:" + PROPERTIES_PATTERN_TEMPLATE + ")*)";

主要方法的输出:

Split parsing:
TMST = 1234567890
GROUP = group1
VARIABLE = varname1
VALUE = 133333337
prop1 = val1
prop2 = val2
prop3 = val3
Execution time: 8695796ns

Regex parsing:
TMST = 1234567890
GROUP = group1
VARIABLE = varname1
VALUE = 133333337
prop1 = val1
prop2 = val2
prop3 = val3
Execution time: 1250787ns

从输出(我多次运行)判断,似乎正则表达式在性能方面更有效,即使我最初的想法更倾向于分裂方法。但是,我不确定这种性能分析的代表性。

我的问题是:

  • 对于无效的输入处理,这两种方法中哪一种最好或更容易使用? (例如:静态项目缺失,格式无效等)。
  • 这些方法中哪一种不太可能产生意外行为?
  • 为什么正则表达式方法更快?我会假设相反,因为MatcherPattern s必须有一些更复杂的逻辑。我的绩效分析是否具有代表性?

2 个答案:

答案 0 :(得分:1)

我会尝试解决你的问题:

  • 对于无效的输入处理,这两种方法中哪一种最好或更容易使用? (例如:静态项目缺失,格式无效等)。

我相信Matcher方法,因为您可以简单地遍历已声明的模式数组并在每个模式上使用Matcher#usePattern(Pattern P)。我发现它干净清晰,将所有想要的正则表达式包装在一个地方,然后快速运行它们。

  • 为什么正则表达式方法更快?我会假设相反,因为Matchers和Patterns必须在它们背后有一些更复杂的逻辑。我的绩效分析是否具有代表性?

您正在使用Apache Commons实现拆分。根据{{​​3}},他们使用字符串标记符的专门实现,documentation显示的速度慢于String#split(Str regex)(使用String#indexOf()),也比匹配器和模式慢做法。

  • 以下哪种方法不太可能产生意外行为?

一般性问题,但我采用Apache Commons方法。它的一个安全优势是它可以为您执行nullcheck。引用StringUtils类的描述:&#34;对null安全的String操作。&#34; (引自StringUtils文档,链接发布在第2个问题的答案中)。除此之外,一切都取决于你:)

答案 1 :(得分:0)

最后,在测试并使用它之后,我选择 String Splitting 方法,原因如下:

  • 对于无效的输入处理,这两种方法中哪一种最好或更容易使用? (例如:静态项目缺失,格式无效等)。

通过拆分,我可以很容易地找出分析字符串的哪些部分失败并记录一个精确而有用的警告消息,与正则表达式一样,这样做更加困难。

  • 以下哪种方法不太可能产生意外行为?

由于解析行的内容不需要验证或验证,我发现使用拆分更可靠。对于正则表达式,我总是在内容上出现限制性太强太松散,从而产生意想不到的结果。

使用拆分方法,我只需使用分隔符进行拆分,打包每个键值对,即可。

  • 为什么正则表达式方法更快?我会假设相反,因为MatcherPattern s必须有一些更复杂的逻辑。我的绩效分析是否具有代表性?

感谢Stye's answer部分,特别是有关Split / Match / indexOf实验的有趣参考。