【问题标题】:Parsing a line of data: split vs regex解析一行数据:split vs regex
【发布时间】:2014-03-13 15:55:41
【问题描述】:

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

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

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

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

字符串拆分(使用 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

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

我的问题是:

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

【问题讨论】:

  • 鉴于System.out.println 的电话,我会担心报告的持续时间的有效性。将文本收集到某种字符串生成器中并在 end = System.nanoTime(); 之后打印可能会更好。
  • 确实!原始上下文中的代码将解析的项目添加到List,我将使用该行为调整上面的代码。
  • @AdrianHHH 我根据您的建议编辑了问题。

标签: java regex performance parsing split


【解决方案1】:

我会尽力解决你的问题:

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

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

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

您正在使用拆分的 Apache Commons 实现。根据他们的documentation,他们正在使用字符串标记器的专门实现,如experiment 所示,它比String#split(Str regex)(使用String#indexOf())慢,也比Matcher&Pattern 方法慢。

  • 哪些方法不太可能产生意外行为?

一般问题,但我会使用 Apache Commons 方法。它的一个安全优势是它为您执行空检查。引用 StringUtils 类的描述:“对 String 的操作是空安全的。” (引自 StringUtils 文档,链接发布在第二个问题的答案中)。除此之外,这一切都取决于你:)

【讨论】:

  • 那个分裂实验问题真的很有趣。不幸的是,indexOf(...) 技术无法在我的代码中实现(因为它只有一个字符),但我会对结果感到好奇。
【解决方案2】:

最后,经过测试和玩玩后,我选择了String Splitting方法,原因如下:

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

通过拆分,我可以轻松找出分析字符串的哪一部分失败并记录准确且有用的警告消息,而使用正则表达式则更难做到这一点。

  • 哪些方法不太可能产生意外行为?

由于解析出的行的内容不需要验证或验证,我发现使用拆分更可靠。使用正则表达式时,我总是会以对内容的限制过于宽松导致意想不到的结果结束。

使用拆分方法,我只是简单的用分隔符拆分,打包每个Key-Value对,就这样了。

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

感谢 Stye's answer 提供的那部分内容,特别是对拆分/匹配/indexOf 实验的有趣参考。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 2020-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多