Проблемы с PHP preg_match

#php #regex #preg-match

#php #регулярное выражение #preg-match

Вопрос:

preg_match, похоже, реализован крайне неудобным способом. Если я использую группы и не все группы выдают совпадение для определенной строки, то вместо возврата массива совпадений, включая пустые записи (null или «), количество массивов уменьшается. Это, похоже, противоречит документации PHP, в которой указано, что она возвращает пустую строку (или обнуляет с помощью флага). Соответствие элементу массива гарантируется только (пустым или иным), если после группы следует знак вопроса, но это не всегда желательно.

Рассмотрим:

 '/^((d{1,2}):)?(d{1,2})(.(d{1,2}))?$/'
 

Безусловные вложенные группы вообще не вносят вклад в массив совпадений, даже не null или «Если я сделаю вложенную группу условной, это нежелательно изменит семантику.

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

 '/^(d{1,2}:)?(d{1,2})(.d{1,2})?$/'
 

по-прежнему существует проблема, поскольку по какой-то причине последней условной группе не удается сгенерировать пустую запись в массиве совпадений.

Я пытаюсь проверить и отформатировать продолжительность упражнения в часах: минутах.секунды Это соответствует: x (x минут) x: y (x часов и y минут x:y.z (x часов, y минут и z секунд) x.y (x минут и y секунд) и другие форматы не работают, что хорошо.

Просто не представляется удобным способ захватить каждое групповое совпадение и использовать его для создания строки длительности в стандартном формате.

Комментарии:

1. В чем проблема? Вы можете захватить нужные вам группы, не так ли? Что не так с $matches[1] , $matches[2] , и т.д.

2. Вам нужно более внимательно прочитать вопрос, чтобы понять проблему. Какой группе соответствует $matches[2]? Вы не можете определить это, если размер возвращаемого массива совпадений не является постоянным и не соответствует количеству групп. Пустые (null или «) не гарантированы, несмотря на официальное описание PHP.

3. Вы всегда можете проверить, соответствует ли группа isset() . С этим проблем нет.

4. Я мог бы в этом конкретном случае, но это неэлегантно. Мне не нужно проверять этот особый случай. Если бы PHP был реализован так, как задокументировано, мне бы это не понадобилось. Большую озабоченность вызывает более общая проблема. Если для некоторых групп, где совпадение не существует, просто нет значения (в отличие от нулевого или пустого строкового значения), то в общем случае мы не можем полагаться на конкретный индекс / ключ, который будет связан с конкретным совпадением группы. Я думаю, было бы лучше, если бы мы могли помечать группы и получать массив пар ключ => значение, где ключи являются значениями тегов.

Ответ №1:

Отсутствующие ключи могут быть оптимизацией. Часть шаблона может даже не выполняться.

Однако возможны именованные группы, и вы можете отформатировать шаблон:

 $pattern = "(
  ^
  (?:(?<hours>d{1,2}):)?
  (?<minutes>d{1,2})
  (?:.(?<seconds>d{1,2}))?
  $
)Dx";

$data = [
  '42', '21:42', '21:42.1'
];

foreach ($data as $subject) {
    if (preg_match($pattern, $subject, $matches)) {
        var_dump(
            [
                'hours' => (int)($matches['hours'] ?? 0),
                'minutes' => (int)($matches['minutes'] ?? 0),
                'seconds' => (int)($matches['seconds'] ?? 0)   
            ]
        );
    }
}
 

Вывод:

 array(3) {
  ["hours"]=>
  int(0)
  ["minutes"]=>
  int(42)
  ["seconds"]=>
  int(0)
}
array(3) {
  ["hours"]=>
  int(21)
  ["minutes"]=>
  int(42)
  ["seconds"]=>
  int(0)
}
array(3) {
  ["hours"]=>
  int(21)
  ["minutes"]=>
  int(42)
  ["seconds"]=>
  int(1)
}
 

Комментарии:

1. Большое вам спасибо! Да, это то, о чем я тоже подумал, оптимизация, полностью пропущенная. Я также подумал, что лучшим способом будут именованные группы и словарь совпадений, но я не знал, что они уже реализованы в PHP. Это так полезно и полностью решает мою проблему!