Php reg exp: соответствие повторяющейся схеме

Я хочу сопоставить строки, подобные приведенным ниже.

abc|q:1,f:2 cba|q:1,f:awd2,t:3awd,h:gr 

Я использую php и preg_match и preg_match_all с этим выражением.

 /^([az]+)\|([az]+:[a-z0-9]+,?)+$/iU 

Это возвращает только первую часть перед трубой и одну a: 1. Что я делаю неправильно, почему он ведет себя таким образом и как я могу заставить его работать?

 /^([az]+)\|((?:[az]+:[a-z0-9]+,?)+)$/iU 

будет захватывать:

  • часть перед трубой
  • часть после части

Жадная природа квантора «+» делает вашу группу захвата ([az] +: [a-z0-9] +,?) Захватывает только последний набор символов, соответствующих этому регулярному выражению.

 /(?ms)^((?:[az]+)\|(?:[az]+:[a-z0-9]+,?)+)$/iU 

захватит всю линию.

Обратите внимание на « ?: », Чтобы избежать создания какой-либо группы захвата .

Я просто попробовал:

 <?php $string = 'cba|q:1,f:awd2,t:3awd,h:gr'; $subpat = '[az]+:[a-z0-9]+'; $pat = "/^([az]+)\|($subpat(?:,$subpat)+)$/i"; preg_match( $pat, $string, $matches ); print_r( $matches ); ?> 

который дает

 Array ( [0] => cba|q:1,f:awd2,t:3awd,h:gr [1] => cba [2] => q:1,f:awd2,t:3awd,h:gr ) 

На этом этапе у вас есть часть перед вертикальной полосой в matches[1] а остальные – в matches[2] . Повторение $subpat существует, чтобы гарантировать правильное разделение строк запятыми. После этого примените взрыв в matches[2] .

 $string = 'cba|q:1,f:awd2,t:3awd,h:gr'; $re = '~(?: ^(\w+)\| ) | (?: (\w+) : (\w+) (?:,|$) )~x'; preg_match_all($re, $string, $m, PREG_SET_ORDER); var_dump($m); 

это будет соответствовать части перед трубой («ведущий») и всем парам ключ-значение одновременно. «lead» будет в $m[0][1] а значения ключа будут в $m[1..x][2] and [3] . Добавьте некоторую простую пост-обработку, чтобы преобразовать ее в полезную форму, например:

 $lead = $m[0][1]; foreach(array_slice($m, 1) as $p) $data[$p[2]] = $p[3]; var_dump($lead, $data);