Как я могу исправить свое регулярное выражение, чтобы оно не слишком сильно совпадало с жадным квантификатором?[дубликат]
-
05-07-2019 - |
Вопрос
На этот вопрос уже есть ответ здесь:
У меня есть следующая строка:
"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)"
Я разбираю это с помощью простого регулярного выражения:
if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) {
my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5);
}
Но тот ;в конце все портится, и я не знаю почему.Разве жадный оператор не должен обрабатывать "все"?
Решение
Жадный оператор пытается захватить как можно больше информации и при этом сопоставить строку.Что происходит, так это то, что первый (после "сказать") берет "0ed673079715c343281355c2a1fde843;2", второй берет "laka", третий находит "hello ", а четвертый соответствует скобке.
Что вам нужно сделать, это сделать все, кроме последнего, нежадными, чтобы они захватывали как можно меньше и по-прежнему соответствовали строке:
(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)
Другие советы
(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)
должно работать лучше
Хотя регулярное выражение может легко сделать это, я не уверен, что это самый прямолинейный подход.Вероятно, он самый короткий, но на самом деле это не делает его самым ремонтопригодным.
Вместо этого я бы предложил что-то вроде этого:
$x="14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)";
if (($ts,$rest) = $x =~ /(\d+:\d+)\s+(.*)/)
{
my($command,$hash,$pid,$handle,$quote) = split /;/, $rest, 5;
print join ",", map { "[$_]" } $ts,$command,$hash,$pid,$handle,$quote
}
Это приводит к:
[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)]
Я думаю, что это просто немного более читабельно.Мало того, я думаю, что это также проще в отладке и обслуживании, потому что это ближе к тому, как вы бы это сделали, если бы человек попытался проделать то же самое с ручкой и бумагой.Разбейте строку на фрагменты, которые затем вам будет проще разобрать - пусть компьютер сделает именно то, что сделали бы вы.Когда придет время вносить изменения, я думаю, что с этим все будет лучше.ИММВ.
Попробуйте сделать первые 3 (.*)
не жадный (.*?)
Если значения в вашем списке, разделенном точкой с запятой, сами по себе не могут содержать никаких точек с запятой, вы получите наиболее эффективное и простое регулярное выражение, просто написав его по буквам.Если определенные значения могут быть, скажем, только строкой шестнадцатеричных символов, произнесите это по буквам.Решения, использующие ленивую или жадную точку, всегда будут приводить к большому количеству бесполезных возвратов, когда регулярное выражение не соответствует строке subject .
(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+)
Вы могли бы сделать * нежадным, добавив вопросительный знак:
$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/
или вы можете сопоставить все, кроме точки с запятой, в каждой части, кроме последней:
$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/