2012-05-01 4 views
0

Я пытаюсь извлечь 4 куска информации из строки. Строка - это имя файла с включенным расширением. Первая группа может содержать любые допустимые символы до тех пор, пока не будет достигнуто пространство до второй группы. Вторая группа данных будет содержать 4 номера внутри набора квадратных скобок. Эта группа разделяется первой группой пробелом. Третьей группой может быть 3 или 4 номера, за которыми следует буква «p». Эти группы также разделяются пробелом из предыдущей группы. Последняя группа - это просто расширение файла.Perl Regex - захватить все символы до шаблона

Вот пример:

This, could be ['a'] s(@m)pl3 file name_with any characters [1923] (720p).avi 

Что тогда нужно будет разобран быть:

$1 = This, could be ['a'] s(@m)pl3 file name_with any characters 
$2 = 1923 
$3 = 720p 
$4 = avi 
+0

Я только понял, насколько непонятно, что я изначально был. Я также заметил, что у меня была ошибка в моих предыдущих предположениях. Я пошел вперед и полностью переформулировал его для ясности. Надеюсь, это поможет, извините за предыдущую путаницу. – TehTechGuy

+0

Извините, это была моя неряшливость, когда я редактировал комментарий. Эти теги были добавлены, и я забыл удалить теги close. – TehTechGuy

ответ

3

Смотрите также perldoc perlreref.

Вот обновленный пример, чтобы принять во внимание ваш образец строки:

#!/usr/bin/env perl 

use strict; use warnings; 

my $x = q{This, could be ['a'] s(@m)pl3 file name_with any characters [1923] (720p).avi}; 

my $pat = qr{ 
    \A 
    (.+?) 
    [ ] 
    \[ ([0-9]{4}) \] 
    [ ] 
    \(([0-9]+ p) \) 
    [.] 
    (.+) 
    \z 
}x; 

print "---$_---\n" for $x =~ $pat; 

Выход:

---This, could be ['a'] s(@m)pl3 file name_with any characters--- 
---1923--- 
---720p--- 
---avi---
+0

не "(любой набор символов)" просто (. *?) – MarcoS

+0

@MarcoS да, но это также соответствует пробелам и символам табуляции, которые предположительно не являются частью имени файла. – Sylverdrag

+0

Я как-то читал это как любые * непространственные * символы, но я мог ошибаться. –

1

Я хотел бы написать регулярное выражение, как этот (.*?) (\[\d{4}\]) (\(\d+p\))\.(.*)

не проверял , и его можно было бы написать лучше :)

+0

Спасибо! Это делает именно то, что мне нужно для этого, как указано в моем первоначальном ответе. Я фактически отредактировал это так: (. *?) \ [(\ D {4}) \] \ ((\ d + p) \) \. (. *) После того, как понял, что мне не нужен квадрат скобки и круглые скобки. – TehTechGuy

0

Я не использую Per л, так что мой Regex может потребоваться некоторые настройки, но AFAIK:

(any set of characters) = \S* 
(a space) = \s+ 
('[' + 4 numbers + ']') = \[[0-9]{4} 
(a space) = \s+ 
('(' + an unknown number of numbers + 'p)') = \([0-9]+p\) 
(a period) = \. 
(file extension) = .{2,5} 
0

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

$x = 'a b c d e [1234] (1080p).mov' 

Теперь вы можете написать регулярное выражение для разбора, но с различными персонажами и сложным регулярным выражением, это может быть болезненным, чтобы поддерживать и легко ломаются. Так почему бы не упростить и использовать split?

my @fields = split ' ', $x; 

Можно также разделить на одном пространстве / /, но тогда вы рискуете несколько пустых полей, если у вас есть несколько пробелов в любом месте. И он не пропускает новые строки.

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

my @nums = grep /\d/, @fields;  # anything with numbers 
my ($tag) = grep /\[\d+\]/, @fields; # catch first [1234] type field 

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

Если вы полагаетесь делать матчи с конца строки в обратном направлении, вы можете использовать reverse функции в сочетании с split, например:

my $xrev = reverse $x; 
my @fields = split ' ', $xrev, 3; 

Если «3» представляет собой предел сколько полей создано, поэтому @fields теперь содержит только три строки.

3

Является ли Perl или нет, иногда проблема с регулярным выражением - это его жадность.Скажем, я хочу, чтобы захватить первое имя кого-то и строка выглядит следующим образом:

Bob Baker 

Я мог бы использовать это регулярное выражение:

sed 's/^\(.*)\ .*$/\1/' 

Это будет работать с Боб Бейкер, но не с Боб Барри Бейкер. Проблема в том, что мое регулярное выражение жадное и выберет все символы до последнего места, поэтому я бы не закончил с Bob, а с Bob Baker. Обычный способ решить это указать все символы, кроме на тот, который вы не хотите:

sed 's/^\([^ ]*)\ .*$/\1/' 

В этом случае, я указав любой набор символов не включая пространство. Это изменит как Bob Baker, так и Bob Rudolph Baker на Bob.

У Perl есть другой способ указать неживое регулярное выражение. В Perl вы добавляете ? к своему суб-выражению, которое вы хотите быть не жадным. В приведенном выше примере, оба из них изменит строку, содержащую Bob Barry Baker только Bob:

$string =~ s/^([^ ]+) .*$/$1/; 
$string =~ s/^(.+?) .*$/$1/; 

Кстати, это не эквивалент!

С все, кроме космического регулярного выражения, я мог бы сделать это:

$string =~ /^([^ ]+)()(\[\d{4}\])()(\(\d+p\))(\.)([^.]+)/ 

С нежадным классификатором:

$string =~ /^(.+?)()(\[\d{4}\])()(\(\d+p\))(\.)(.*)/ 

И, используя x классификатор, который позволяет вам поместите одно и то же регулярное выражение на несколько строк, что приятно, потому что вы можете добавлять комментарии, чтобы помочь объяснить, что вы делаете:

$string =~/
    ^(.+?)     #Any set of characters (non-greedy) 
    ([ ])     #Space 
    (\[\d{4}\])    #[1959] 
    ([ ])     #Space 
    (\([0-9]+p\))   #(430p) 
    [.]      #Period 
    ([^\.]+)     #File Suffix (no period) 
/x 

И в этот момент вы могли бы также следовать рекомендациям Даниина Конвей Рекомендации по регулярным выражениям на языке Перл.

$string =~/
    \A     #Start of Regular Expression Anchor 
    (.+?)   #Any set of characters (non-greedy) 
    ([ ])   #Space 
    (\[ \d{4} \]) #[1959] 
    ([ ])   #Space 
    (\([0-9] +p \)) #(430p) 
    ([.])   #Period 
    ([^\.]+)   #File Suffix (no period) 
    \Z     #End of string anchor 
/xm; 

Поскольку x игнорирует всех белого пространства, я даже могу добавить пробелов между подгруппами по одной и той же линии. В этом случае (.*+?) немного чище, чем (.*+?). (\([0-9] +p \)) или (\([0-9]+p \)) или даже (\([0-9]+p\)) проще понять, зависит от вас.

И, да, ответ очень похож на Sinan's ответ.

Кстати, как показало Sinan, используя нежадный регулярные выражения спецификатора способно анализировать a b c d e [1234] (1080p).mov при использовании все, что не включает в себя подвыражение пространства не будет.Вот почему я сказал, что они не то же самое.

+0

Ваше объяснение вещей было очень полезно. Я согласен с тем, что комментарии в стороне облегчают интерпретацию происходящего. Я заметил, что вам не хватает закрывающей скобки на строке «#Period» последнего блока кода. Я также не мог заставить его работать, используя «$ string = ~ /», но после его изменения в «$ string = qr» он работал. – TehTechGuy

+0

'$ string = ~ /../' предполагает, что строка, которую вы обрабатываете, является '$ string'. 'My $ regex = qr (..)' сохраняет регулярное выражение в 'regex', поэтому вы можете использовать' $ string = ~/$ regex /; 'позже. Я исправил недостающие скобки. Такие ошибки случаются, когда вы копируете материал вручную. –

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