Разбор строки данных: split против регулярного выражения

1

У меня есть строки данных, поступающих из сценария, который обычно выглядит так (пример с одной строкой):

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

Мне нужно разбить каждую строку на элементы Key-Value для 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;
}

Regex (с использованием 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

Судя по результату (который я запускал несколько раз), кажется, что метод regex более эффективен с точки зрения производительности, хотя мои первоначальные мысли были более похожими на метод расщепления. Однако я не уверен, насколько репрезентативен этот анализ производительности.

Мои вопросы:

  • С какими из этих двух методов было бы лучше или проще работать с недопустимой обработкой ввода? (Пример: статический элемент отсутствует, неверный формат и т.д.).
  • Какой из этих методов менее вероятно приведет к неожиданному поведению?
  • Почему метод regex быстрее? Я бы принял противоположное, поскольку у Matcher и Pattern должна быть несколько более сложная логика. Является ли мой анализ эффективности даже представительным?
Теги:
performance
split
parsing

2 ответа

0
Лучший ответ

В конце концов, после тестирования и игры с ним я пошел с методом String Splitting по следующим причинам:

  • С какими из этих двух методов было бы лучше или проще работать с недопустимой обработкой ввода? (Пример: статический элемент отсутствует, неверный формат и т.д.).

При расщеплении я могу легко выяснить, какая часть анализируемой строки терпит неудачу, и записывать точное и полезное предупреждающее сообщение, где, как и в случае с регулярными выражениями, сделать это сложнее.

  • Какой из этих методов менее вероятно приведет к неожиданному поведению?

Поскольку содержимое анализируемой строки не нужно проверять или проверять, я счел более надежным использовать разделение. С регулярными выражениями у меня всегда было что-то слишком ограничивающее содержание или что-то слишком свободное, что давало неожиданные результаты.

С помощью метода расщепления я просто разделился с разделителем, упаковал каждую пару "ключ-значение" и это сделал.

  • Почему метод regex быстрее? Я бы принял противоположное, поскольку у Matcher и Pattern должна быть несколько более сложная логика. Является ли мой анализ эффективности даже представительным?

Благодаря ответу Stye для этой части, особенно с интересной ссылкой на эксперимент Split/Match/indexOf.

1

Я постараюсь решить ваши вопросы:

  • С какими из этих двух методов было бы лучше или проще работать с недопустимой обработкой ввода? (Пример: статический элемент отсутствует, неверный формат и т.д.).

Я считаю, метод Matcher, потому что вы можете просто перебрать свой объявленный массив шаблонов и использовать Matcher#usePattern(Pattern P) для каждого. Я нахожу его чистым и понятным, упаковывая все нужные регулярные выражения в одном месте и запуская их быстро для каждого.

  • Почему метод regex быстрее? Я бы предпочел обратное, поскольку у Matchers и Patterns должна быть несколько более сложная логика. Является ли мой анализ эффективности даже представительным?

Вы используете реализацию Apache Commons для split. В своей документации они используют специализированную реализацию String Tokenizer, которая, как показывает эксперимент, медленнее, чем String#split(Str regex) (которая использует String#indexOf()), а также медленнее, чем подход Matcher & Pattern.

  • Какой из этих методов менее вероятно приведет к неожиданному поведению?

Общий вопрос, но я бы пошел с Apache Commons. Одно из преимуществ безопасности - для вас это делает nullchecks. Цитирование описания класса StringUtils: "Операции над строкой, которые являются нулевыми". (цитируется из документации StringUtils, ссылка размещена в ответ на второй вопрос). Кроме этого, все зависит от вас:)

  • 0
    Этот вопрос эксперимента расщепления действительно интересен. К сожалению, метод indexOf(...) не может быть реализован в моем коде (так как это всего один символ), но мне было бы любопытно получить результаты.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню