У меня есть строка 1/temperatoA,2/CelcieusB!23/33/44,55/66/77
, и я хотел бы извлечь слова temperatoA
и CelcieusB
.
У меня есть это регулярное выражение (\d+/(\w+),?)*!
, но я получаю только совпадение 1/temperatoA,2/CelcieusB!
Почему?
У меня есть строка 1/temperatoA,2/CelcieusB!23/33/44,55/66/77
, и я хотел бы извлечь слова temperatoA
и CelcieusB
.
У меня есть это регулярное выражение (\d+/(\w+),?)*!
, но я получаю только совпадение 1/temperatoA,2/CelcieusB!
Почему?
Ваше полное совпадение оценивается как '1/temperatoA,2/CelcieusB'
, поскольку оно соответствует следующему выражению:
qr{ ( # begin group
\d+ # at least one digit
/ # followed by a slash
(\w+) # followed by at least one word characters
,? # maybe a comma
)* # ANY number of repetitions of this pattern.
}x;
'1/temperatoA,'
сначала выполняет захват № 1, но, поскольку вы просите движок захватить как можно больше из них, он возвращается и обнаруживает, что шаблон повторяется в '2/CelcieusB'
(запятая не нужна). Таким образом, все совпадение соответствует вашим словам, но вы, вероятно, не ожидали, что '2/CelcieusB'
заменяет '1/temperatoA,'
на $1
, поэтому $1
читается как '2/CelcieusB'
.
Каждый раз, когда вы хотите захватить что-либо, что соответствует определенному шаблону в определенной строке, всегда лучше использовать флаг global и назначать захваты в массив. Поскольку массив не является одним скаляром, таким как $1
, он может содержать все значения, которые были захвачены для захвата № 1.
Когда я делаю это:
my $str = '1/temperatoA,2/CelcieusB!23/33/44,55/66/77';
my $regex = qr{(\d+/(\w+))};
if ( my @matches = $str =~ /$regex/g ) {
print Dumper( \@matches );
}
Я получаю это:
$VAR1 = [
'1/temperatoA',
'temperatoA',
'2/CelcieusB',
'CelcieusB',
'23/33',
'33',
'55/66',
'66'
];
Теперь, я думаю, это, вероятно, не то, что вы ожидали. Но '3'
и '6'
являются словными символами, и поэтому, идущие после косой черты, соответствуют выражению.
Итак, если это проблема, вы можете изменить свое регулярное выражение на эквивалентное: qr{(\d+/(\p{Alpha}\w*))}
, указав, что первый символ должен быть альфа, за которым следует любое количество символов слова. Тогда дамп выглядит так:
$VAR1 = [
'1/temperatoA',
'temperatoA',
'2/CelcieusB',
'CelcieusB'
];
И если вам нужно только 'temperatoA'
или 'CelcieusB'
, то вы захватываете больше, чем вам нужно, и вам нужно, чтобы ваше регулярное выражение было qr{\d+/(\p{Alpha}\w*)}
.
Однако секрет захвата более чем одного фрагмента в выражении захвата заключается в назначении совпадения массиву, затем вы можете отсортировать массив, чтобы увидеть, содержит ли он нужные вам данные.
Возникает вопрос: почему вы используете регулярное выражение, которое явно неверно? Как ты получил это?
Выражение, которое вы хотите, просто выглядит следующим образом:
(\w+)
С Perl-совместимым механизмом регулярных выражений вы можете искать
(?<=\d/)\w+(?=.*!)
(?<=\d/)
утверждает, что перед началом совпадения стоит цифра и косая черта
\w+
соответствует идентификатору. Это позволяет использовать буквы, цифры и знак подчеркивания. Если вы хотите разрешить только буквы, используйте вместо этого [A-Za-z]+
.
(?=.*!)
утверждает, что впереди в строке есть !
- т.е. е. регулярное выражение завершится ошибкой, как только мы пройдем !
.
В зависимости от используемого языка вам может потребоваться экранировать некоторые символы в регулярном выражении.
Например, для использования в C (с библиотекой PCRE) вам нужно экранировать обратную косую черту:
myregexp = pcre_compile("(?<=\\d/)\\w+(?=.*!)", 0, &error, &erroroffset, NULL);
Будет ли это работать?
/([[:alpha:]]\w+)\b(?=.*!)
Я сделал следующие предположения...
[[:alpha:]]
соответствует любому буквенному символу.
\b
соответствует границе слова.
А (?=.*!)
взято из сообщения Тима Питцкера.