Регулярные выражения Java с использованием Pattern и Matcher - PullRequest
4 голосов
/ 29 сентября 2010

Мой вопрос связан с регулярными выражениями в Java и, в частности, с несколькими совпадениями для данного шаблона поиска.Вся информация, которую мне нужно получить, находится в 1 строке и содержит псевдоним (например, SA), который сопоставляется с IP-адресом.Каждый разделен запятой.Мне нужно извлечь каждый.

SA "239.255.252.1", SB "239.255.252.2", SC "239.255.252.3", SD "239.255.252.4"

Мой Reg Ex выглядит следующим образом:

Pattern alias = Pattern.compile("(\\S+)\\s+\"(\\d+\\.\\d+\\.\\d+\\.\\d+)\"");  
Matcher match = alias.matcher(lineInFile)  
while(match.find()) {  
   // do something  
}

Это работает, но я не совсем доволен этим, потому что с момента появления этого небольшого фрагмента кода моя программа замедлиласьнемного (<1 сек), но достаточно, чтобы заметить разницу. </p>

Итак, мой вопрос, правильно ли я поступаю?Есть ли более эффективное или, возможно, более легкое решение без необходимости использования цикла while (match)?и / или классы Pattern / Matcher?

Ответы [ 6 ]

1 голос
/ 29 сентября 2010

Если строка не может содержать ничего, кроме определения псевдонима, то использование .match() вместо .find() может ускорить поиск несоответствий.

0 голосов
/ 29 сентября 2010

Если вы заметили разницу в <1 с на этом куске кода, то ваша входная строка должна содержать около миллиона (или не менее 100 000) записей. Я думаю, что это довольно неплохая производительность, и я не могу понять, как вы могли бы существенно оптимизировать это, не написав свой собственный специализированный парсер. </p>

0 голосов
/ 29 сентября 2010

Предварительная компиляция и повторное использование объекта Pattern (IMO), вероятно, будет наиболее эффективной оптимизацией.Компиляция шаблона - потенциально дорогой шаг.

Повторное использование экземпляра Matcher (например, использование reset(CharSequence)) может помочь, но я сомневаюсь, что это будет иметь большое значение.

Само регулярное выражение не может быть существенно оптимизировано.Одним из возможных ускорений было бы заменить (\d+\.\d+\.\d+\.\d+) на ([0-9\.]+).Это может помочь, потому что это уменьшает количество потенциальных точек возврата ... но вам нужно будет провести несколько экспериментов, чтобы быть уверенным.И очевидным недостатком является то, что он соответствует последовательности символов, которые не являются действительными IP-адресами.

0 голосов
/ 29 сентября 2010

Я не знаю, приведет ли это к большому выигрышу в производительности, но вы могли бы сначала сделать

string.split(", ") // separate groups

, а затем

string.split(" ?\"") // separate alias from IP address

на матчах.

0 голосов
/ 29 сентября 2010

Вы можете улучшить свое регулярное выражение до: "(\\S{2})\\s+\"((\\d{1,3}\\.){3}\\d{1,3})\"", указав IP-адрес более явно.

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

StringTokenizer st = new StringTokenizer(lineInFile, " ,\"");
while(st.hasMoreTokens()){
    String key = st.nextToken();
    String ip = st.nextToken();
    System.out.println(key + " ip: " +  ip);
}
0 голосов
/ 29 сентября 2010

Боюсь, ваш код уже выглядит довольно эффективно. Вот моя версия:

Matcher match = Pattern
                .compile("(\\w+)\\s+\"(\\d+\\.\\d+\\.\\d+\\.\\d+)\"")
                .matcher(lineInFile);  
while(match.find()) {  
    //do something  
}

Есть две микрооптимизации:

  1. Нет необходимости хранить шаблон в дополнительном переменная, встраиваемая что
  2. Для псевдонима, поиск слова символы, а не пробелы

На самом деле, если вы выполняете много подобной обработки, и шаблон никогда не меняется, вы должны сохранять скомпилированный шаблон постоянным:

private static final Pattern PATTERN = Pattern
            .compile("(\\w+)\\s+\"(\\d+\\.\\d+\\.\\d+\\.\\d+)\"");

Matcher match = PATTERN.matcher(lineInFile);  
while(match.find()) {  
    //do something  
}

Обновление: я потратил некоторое время на RegExr , чтобы придумать гораздо более конкретный шаблон, который должен определять только действительные IP-адреса в качестве бонуса. Я знаю, что это чертовски уродливо, но я предполагаю, что это довольно эффективно, так как устраняет большую часть возврата:

([A-Z]+)\s*\"((?:1[0-9]{2}|2(?:(?:5[0-5]|[0-9]{2})|[0-9]{1,2})\.)
{3}(?:1[0-9]{2}|2(?:5[0-5]|[0-9]{2})|[0-9]{1,2}))

(Обернутый для удобства чтения, все обратные слэши необходимо экранировать в java, но вы можете протестировать его на RegExr, как и в тестовой строке OP) *

...