Использование регулярного выражения для извлечения слова, если оно существует

Я хочу использовать RE для анализа файла журнала и возврата идентификатора заказа, если он существует. Например:

Вот пример журнала

2012-07-19 12:05:04,288 [22] INFO  AddQueueCommand [(null)] - Status set to Subscribed
2012-07-19 12:05:04,288 [23] INFO  FooBarProviderFactory [(null)] - Missing Function : OrderId:102602 : Method:AddOrderToId : application:11
2012-07-19 12:05:04,288 [22] INFO  AddQueueCommand [(null)] - Status set to Pending
2012-07-19 12:05:04,288 [23] INFO  AddSubscription [(null)] - Subscription Added. OrderId:102603 : application:15
2012-07-19 12:05:04,288 [22] INFO  AddQueueCommand [(null)] - Status set to Subscribed

Что я хочу сделать, так это использовать регулярное выражение, чтобы я мог анализировать компоненты сообщения журнала. Но когда существует «OrderId», я хочу иметь возможность анализировать идентификатор заказа #.

Вот что у меня есть до сих пор:

^
(?<before>.*)
(?<order>((?<=OrderId\:\s*)\d*))
(?<after>.*)
$

который отлично работает для анализа идентификаторов заказов для строк, в которых они есть, но терпит неудачу, когда в строке их нет. Я попытался добавить "?" ноль или единица в строке заказа, которая затем анализирует все строки, но никогда не анализирует фактический идентификатор заказа. Они всегда нулевые.

Надеюсь, кто-нибудь увидит, что я делаю неправильно. Спасибо!

(Я хочу, чтобы он анализировал каждую строку, потому что я собираюсь анализировать несколько значений идентификаторов из каждой строки, и они могут существовать или не существовать. Я хочу, чтобы он возвращал значение, если то, что я ищу, существует или null/пусто, если это не так) t существует. Он должен что-то возвращать для каждой строки. Это будет подключено к LogParser, чтобы мы могли запрашивать или регистрировать определенные заказы или другие переменные)


person dan27    schedule 20.07.2012    source источник
comment
На чем написан LogParser?   -  person Tim Pietzcker    schedule 20.07.2012
comment
Не знаю, на чем написан LogParser, но я использовал пользовательский интерфейс Logparser Lizard (lizard-labs.net/ log_parser_lizard.aspx)   -  person dan27    schedule 21.11.2012


Ответы (3)


Если вы сделаете группу <order> необязательной, то группа <before> всегда будет соответствовать всей строке, поэтому совпадение будет успешным без захвата OrderId, даже если он есть. Облегчение не поможет ((?<before>.*?)) в этом случае (потому что тогда группа <after> соответствует всему).

Но вы можете делать все, что хотите, в одном регулярном выражении, если я правильно вас понимаю. Например, предположим, что вам нужно слово после Status set to (если есть) и число после OrderId: (если есть) для каждой строки, тогда вы можете использовать регулярное выражение

^
(?(?=.*Status\sset\sto\s)(?=.*Status\sset\sto\s(?<status>\w+))|)
(?(?=.*OrderId:)(?=.*OrderId:(?<order>\d+))|)

в каждой строке и проверьте, совпадают ли группы <status> и/или <order>. Расширяйте по мере необходимости.

Это предполагает, что ваш движок регулярных выражений поддерживает условия, как в случае .NET, Perl и ПКРЕ.

Пояснение:

(?               # Conditional: IF it's possible to match...
 (?=.*OrderId:)  #  any string, followed by "OrderId:"
                 # THEN try to match this:
  (?=            #  Lookahead assertion:
   .*OrderId:    #   any string, followed by "OrderId:" 
   (?<order>\d+) #   followed by a number --> capture in group <status>
  )              #  End of lookahead
 |               # ELSE try to match this:
                 #  the empty string (always succeeds)
)                # End of conditional.

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

person Tim Pietzcker    schedule 20.07.2012
comment
Спасибо, Тим. Это сработало прекрасно! Именно то, что я искал. - person dan27; 20.07.2012
comment
Привет, Тим, еще одно быстрое дополнение. В журнале иногда идентификатор приложения выглядит как ...application:11... а иногда как ...ApplicationId: 11.... Используя ваш отзыв, я смог получить его, используя следующее, но просто интересно, может ли это быть чище, так как мне пришлось повторять \s*(?‹AppId›\d+). просто интересуюсь. (?(?=.*ApplicationId:|.*application:)(?=.*ApplicationId:\s*(?‹AppId›\d+)|.*application:\s*(?‹AppId›\d+)) |) - person dan27; 20.07.2012
comment
Я бы предложил (?(?=.*Application(?:Id)?:)(?=.*Application(?:Id)?:\s*(?<AppId>\d+))|) (при компиляции регулярного выражения с параметром без учета регистра или добавлением (?i) в начале). - person Tim Pietzcker; 20.07.2012

Используйте метод сканирования строк

Если все, что вам нужно, это номера записей OrderId, вы можете упростить эту задачу, просканировав строку на наличие совпадающего выражения. Например, если данные вашего журнала хранятся в строке log, в Ruby вы можете сделать следующее:

log.scan /OrderId:(\d+)/
=> [["102602"], ["102603"]]

Будет сохранен только совпавший текст. Никакие значения массива не будут сохранены для строк без соответствия.

person Todd A. Jacobs    schedule 20.07.2012


Вы можете вернуть список идентификаторов со списком понимания:

 >>> import re
 >>> [ re.sub( r".*OrderId:(\d*).*", r"\1", line ) for line in logs.readlines() if 'OrderId' in line ]
 ['102602', '102603']
person Zulu    schedule 20.07.2012