2010-09-29 1 views
4

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

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 сек), но достаточно заметить разницу.

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

+0

Большое спасибо всем, кто нашел время, чтобы ответить на мой запрос. Это было очень полезным упражнением для меня, поскольку я все еще очень начинаю Java. Кстати, я добавил еще более конкретную информацию о шаблоне RegExp, и это, казалось, помогло. Есть известная строка («псевдонимы») перед первой парой псевдонимов/IP, которую я положил, и это определенно помогло. Еще раз спасибо ребятам! – Wilko

ответ

1

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

0

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

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 
} 

Update: Я взял некоторое время на 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)

+0

Эффект первой микро-оптимизации, вероятно, слишком мал для измерения. Второй изменяет значение регулярного выражения, и неясно, что он помогает. Однако предварительная компиляция и повторное использование шаблона определенно стоит. –

+0

Повторите вторую микро-оптимизацию: глядя на код класса Pattern, я думаю, что это может привести к замедлению работы шаблона! –

+0

Да, это имеет смысл. Поиск отрицания небольшого символьного класса должен быть более эффективным, чем поиск класса символов lqrge, такого как \ w. Я скоро обновлю свой ответ –

0

Вы можете улучшить свое регулярное выражение до: "(\\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

* StringTokenizer - это унаследованный класс, который сохраняется по соображениям совместимости, хотя его использование не рекомендуется в новом коде. Рекомендуется, чтобы любой, кто ищет эту функциональность, использовал метод split для String или вместо java.util.regex. * (Источник: http://download.oracle.com/javase/6/docs/api/java/util/ StringTokenizer.html) –

+0

Тем не менее, сканер может быть хорошей альтернативой: http://download-llnw.oracle.com/javase/6/docs/api/java/util/Scanner.html –

+0

Да, я знаю. Вот почему я поставил заметку в своем посте. StringTokenizer использует внутренние методы String 'indexOf' и' substring', поэтому мы можем видеть, как он работает и реплицировать его функциональность в нашем новом коде, если он быстрее, чем регулярное выражение. – dogbane

0

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

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

, а затем

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

на матчи.

+0

Итак, два регулярных прохода будут быстрее одного? Я сомневаюсь в этом. –

+0

@seanizer: Я тоже сомневаюсь. Я не использую Java, поэтому я не могу его профилировать. Но, возможно, стоит попробовать. –

0

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

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

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

+0

Повторное использование матчи звучит как плохая идея, потому что это действительно повредит вещи в многопоточном сценарии (если вы не представите пулы объектов и *, которые * действительно будут переполнены) –

0

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

Смежные вопросы