Как я могу указать дополнительную группу захвата в этом RegEx? - PullRequest
5 голосов
/ 28 января 2009

Как я могу исправить это RegEx, чтобы дополнительно захватить расширение файла?

Я пытаюсь сопоставить строку с необязательным компонентом, но что-то не так. (Соответствующие строки взяты из журнала принтера.)


Мой RegEx (.NET Flavor) выглядит следующим образом:

.*(header_\d{10,11}_).*(_.*_\d{8}).*(\.\w{3,4}).*
-------------------------------------------
.*                   # Ignore some garbage in the front
(header_             # Match the start of the file name,
    \d{10,11}_)      #     including the ID (10 - 11 digits)
.*                   # Ignore the type code in the middle
(_.*_\d{8})          # Match some random characters, then an 8-digit date
.*                   # Ignore anything between this and the file extension
(\.\w{3,4})          # Match the file extension, 3 or 4 characters long
.*                   # Ignore the rest of the string


Я ожидаю, что это будет соответствовать строкам вроде:

str1 = "header_0000000602_t_mc2e1nrobr1a3s55niyrrqvy_20081212[1].doc [Compatibility Mode]"
str2 = "Microsoft PowerPoint - header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1].txt"
str3 = "header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1]"


Где группы захвата возвращают что-то вроде:

$1  =  header_0000000602_
$2  =  _mc2e1nrobr1a3s55niyrrqvy_20081212
$3  =  .doc


Где $ 3 может быть пустым, если расширение файла не найдено. $ 3 является необязательной частью, как вы можете видеть в str3 выше.

Если я добавлю "?" до конца третьей группы захвата "(. \ w {3,4})?", RegEx больше не захватывает $ 3 для какой-либо строки. Если я добавлю «+» вместо «(. \ W {3,4}) +», RegEx больше не будет захватывать str3, чего и следовало ожидать.

Я чувствую, что используя "?" в конце третьей группы захвата - подходящая вещь, но она не работает, как я ожидаю. Возможно, я слишком наивен с разделами ". *", Которые я использую для игнорирования частей строки.


Не работает, как ожидалось:

.*(header_\d*_).*(_.*_.{8}).*(\.\w{3,4})?.*

Ответы [ 7 ]

5 голосов
/ 28 января 2009

Одна возможность состоит в том, что второй до последнего .* является жадным. Вы можете попробовать изменить его на:

.*(header_\d*_).*(_.*_.{8}).*?(\.\w{3,4})?.*
                             ^ Added that

Это не правильно, этот будет соответствовать введенному вами вводу, но предполагается, что первый ., с которым он сталкивается, является началом расширения файла:

.*(header_\d*_).*(_.*_.{8})[^\.]*(\.\w{3,4})?.*

Редактировать: Удалите спасение, которое я имел во втором регулярном выражении.

3 голосов
/ 28 января 2009

Я полагаю, что проблема в вашем третьем .*, который вы аннотировали выше с помощью «Игнорировать что-либо между этим и расширением файла». Это жадный, поэтому он будет соответствовать НИЧЕГО. Когда вы делаете необязательный шаблон расширения, 3-й .* совпадает до конца строки, что разрешено. Предполагая, что НИКОГДА не будет символа '.' в этом постороннем бите, вы можете заменить .* на [^.]*, и, надеюсь, все остальное будет работать после восстановления ?, который вам пришлось удалить.

2 голосов
/ 28 января 2009

Это ваш правильный результат

.*?(header_\d*_).*?(_.*_.{8})[^.]*(\.\w{3,4})?.*
-------------------------------------------
.*?                  # Prevent a greedy match
(header_             # 
    \d{10,11}_)      # 
.*?                  # Prevent a greedy match
(_.*_\d{8})          # 
[^.]*                # Take everything that is NOT a period
(\.\w{3,4})          # Match the extension
.*                   # 

Неявное предположение состоит в том, что точка будет началом расширения файла после совпадения цифр. Следующее не соответствует этому требованию:

string unmatched = "header_00000000076_d_al41zguyvgqfj2454jki5l55_20071203[1].foobar.txt"

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

regex.Match(string_to_match).Groups[1].Value
regex.Match(string_to_match).Groups[2].Value
regex.Match(string_to_match).Groups[3].Value

а не это:

// 0 index == string_to_match
regex.Match(string_to_match).Groups[0].Value
regex.Match(string_to_match).Groups[1].Value
regex.Match(string_to_match).Groups[2].Value

Это то, что сперва меня сбило с толку.

2 голосов
/ 28 января 2009

Укажите во втором совпадении, что вы хотите сопоставить только все символы, у которых нет точки , затем соответствует вашему расширению.

".*(header_\d{10,11}_).*(_.*_\d{8})[^.]*(\.\w{3,4})?"
2 голосов
/ 28 января 2009

Что ж, .*, вероятно, неправильный способ запуска регулярного выражения - он будет соответствовать 0 или более (*) одиночным символам чего угодно (.) ... что означает, что все ваше имя файла будет соответствовать этому в одиночестве. Если вы не включите это, регулярное выражение начнет совпадать, когда достигнет header, что вы и хотите. Вы также можете заменить его на \w, что соответствует переносу слов. Я также предлагаю использовать такой инструмент, как Regex Coach , чтобы вы могли пройти через него и точно увидеть, что не так и какими будут ваши группы захвата.

1 голос
/ 28 января 2009

Вот тот, который работает для того, что вы публикуете:

^.*(?<header>header_\d{10,11})_.*(?<date>_[a-z0-9]+_\d{8})(\[\d+\])(?<ext>(\.[a-zA-Z0-9]{3,4})?).*

Замена:

Header: $1
Date: $2
Extension: $4

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

1 голос
/ 28 января 2009

Это работает для примеров, которые вы разместили:

^.*?(?<header>\d+)_.*?_(?<date>\d{8}).*?(?:\.(?<ext>\w{3,4}))?[\w\s\[\]]*$

Я предполагаю, что текстовый заголовок и случайные символы между ним и датой не важны, поэтому они не фиксируются этим регулярным выражением. Для ясности я также использовал функцию захвата имен .NET, но учтите, что она не поддерживается в других разновидностях RegEx.

Если текст после имени файла содержит не алфавитно-цифровые символы, кроме [и], шаблон необходимо будет изменить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...