У меня есть строки данных, поступающих из сценария, который обычно выглядит так (пример с одной строкой):
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 более эффективен с точки зрения производительности, хотя мои первоначальные мысли были более похожими на метод расщепления. Однако я не уверен, насколько репрезентативен этот анализ производительности.
Мои вопросы:
Matcher
и Pattern
должна быть несколько более сложная логика. Является ли мой анализ эффективности даже представительным?В конце концов, после тестирования и игры с ним я пошел с методом String Splitting по следующим причинам:
При расщеплении я могу легко выяснить, какая часть анализируемой строки терпит неудачу, и записывать точное и полезное предупреждающее сообщение, где, как и в случае с регулярными выражениями, сделать это сложнее.
Поскольку содержимое анализируемой строки не нужно проверять или проверять, я счел более надежным использовать разделение. С регулярными выражениями у меня всегда было что-то слишком ограничивающее содержание или что-то слишком свободное, что давало неожиданные результаты.
С помощью метода расщепления я просто разделился с разделителем, упаковал каждую пару "ключ-значение" и это сделал.
Matcher
и Pattern
должна быть несколько более сложная логика. Является ли мой анализ эффективности даже представительным?Благодаря ответу Stye для этой части, особенно с интересной ссылкой на эксперимент Split/Match/indexOf.
Я постараюсь решить ваши вопросы:
Я считаю, метод Matcher, потому что вы можете просто перебрать свой объявленный массив шаблонов и использовать Matcher#usePattern(Pattern P)
для каждого. Я нахожу его чистым и понятным, упаковывая все нужные регулярные выражения в одном месте и запуская их быстро для каждого.
Вы используете реализацию Apache Commons для split. В своей документации они используют специализированную реализацию String Tokenizer, которая, как показывает эксперимент, медленнее, чем String#split(Str regex)
(которая использует String#indexOf()
), а также медленнее, чем подход Matcher & Pattern.
Общий вопрос, но я бы пошел с Apache Commons. Одно из преимуществ безопасности - для вас это делает nullchecks. Цитирование описания класса StringUtils: "Операции над строкой, которые являются нулевыми". (цитируется из документации StringUtils, ссылка размещена в ответ на второй вопрос). Кроме этого, все зависит от вас:)
indexOf(...)
не может быть реализован в моем коде (так как это всего один символ), но мне было бы любопытно получить результаты.