использование регулярных выражений для пропуска всех символов до тех пор, пока не будет найдена определенная последовательность букв с использованием отрицательного взгляда - PullRequest
6 голосов
/ 20 июля 2010

Я в порядке с основными регулярными выражениями, но я немного теряюсь из-за взгляда pos / neg впереди / позади.

Я пытаюсь получить идентификатор # из этого:

[keyword stuff=otherstuff id=123 morestuff=stuff]

Там может быть неограниченное количество "вещи" до или после.Я использовал Regex Coach, чтобы помочь отладить то, что я пробовал, но я больше не продвигаюсь вперед ...

Пока у меня есть это:

\[keyword (?:id=([0-9]+))?[^\]]*\]

Чтозаботится о любых дополнительных атрибутах после идентификатора, но я не могу понять, как игнорировать все между ключевым словом и идентификатором.Я знаю, что не могу пойти [^id]* Я считаю, что мне нужно использовать отрицательный взгляд наподобие этого (?!id)*, но я предполагаю, что поскольку он имеет нулевую ширину, он не движется вперед оттуда.Это также не работает:

\[keyword[A-z0-9 =]*(?!id)(?:id=([0-9]+))?[^\]]*\]

Я искал примеры, но не нашел ни одного.Или, может быть, у меня есть, но они зашли так далеко, что я даже не осознал, кто они.

Помогите!Спасибо.

РЕДАКТИРОВАТЬ: оно также должно соответствовать [ключевое слово stuff = otherstuff], где id = не существует вообще, поэтому я должен иметь 1 или 0 в группе id #.Есть и другие [otherkeywords id = 32], которым я не хочу соответствовать.Документ должен соответствовать нескольким [ключевое слово id = 3] во всех документах, используя preg_match_all.

Ответы [ 3 ]

2 голосов
/ 20 июля 2010

Вам не нужно смотреть вперед / назад.

Поскольку вопрос помечен как PHP, используйте preg_match_all () и сохраните совпадение в $ совпадений.

Вот как:

<?php

  // Store the string. I single quote, in case there are backslashes I
  // didn't see.
$string = 'blah blah[keyword stuff=otherstuff id=123 morestuff=stuff]
           blah blah[otherkeyword stuff=otherstuff id=555 morestuff=stuff]
           blah blah[keyword stuff=otherstuff id=444 morestuff=stuff]';

  // The pattern is '[keyword' followed by not ']' a space and id
  // The space before id is important, so you don't catch 'guid', etc.
  // If '[keyword'  is always at the beginning of a line, you can use
  // '^\[keyword'
$pattern = '/\[keyword[^\]]* id=([0-9]+)/';

  // Find every single $pattern in $string and store it in $matches
preg_match_all($pattern, $string, $matches);

  // The only tricky part you have to know is that each entire match is stored in
  // $matches[0][x], and the part of the match in the parentheses, which is what
  // you want is stored in $matches[1][x]. The brackets are optional, since it's
  // only one line.
foreach($matches[1] as $value)
{     
    echo $value . "<br/>";
}
?>

Вывод:

123
444   

(пропущено 555, как и должно быть)

PS

Вы также можете использовать \b вместо литерального пробела, если вместо него может быть вкладка.\b представляет границу слова ... в данном случае начало слова.

$pattern = '/\[keyword[^\]]*\bid=([0-9]+)/';
2 голосов
/ 20 июля 2010

Не нужно смотреть вперед / назад:

/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/

Добавлено окончание '[^]] *]' для проверки реального конца тега, может быть ненужным.

Редактировать: добавил \ b к идентификатору, так как в противном случае он мог бы соответствовать [keyword you-dont-want-this-guid=123123-132123-123 id=123]

$ php -r 'preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff morestuff=stuff]",$matches);var_dump($matches);'
array(2) {
  [0]=>
  array(1) {
    [0]=>
    string(42) "[keyword stuff=otherstuff morestuff=stuff]"
  }
  [1]=>
  array(1) {
    [0]=>
    string(0) ""
  }
}
$ php -r 'var_dump(preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff id=123 morestuff=stuff]",$matches),$matches);'
int(1)
array(2) {
  [0]=>
  array(1) {
    [0]=>
    string(49) "[keyword stuff=otherstuff id=123 morestuff=stuff]"
  }
  [1]=>
  array(1) {
    [0]=>
    string(3) "123"
  }
}
0 голосов
/ 20 июля 2010

Я думаю, это то, что вы получаете:

\[keyword(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)*(?:\s+id=([0-9]+))?[^\]]*\]

(я предполагаю, что имена атрибутов могут содержать только буквы ASCII, в то время как значения могут содержать любые непробельные символы, кроме ].)

(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)* соответствует любому количеству пар attribute=value (и предшествующих им пробелов), если имя атрибута не равно id. \b (граница слова) есть на всякий случай, если есть имена атрибутов, которые начинаются с id, например idiocy. На этот раз нет необходимости ставить \b перед именем атрибута, потому что вы знаете, что любому имени, которому он соответствует, будет предшествовать пробел. Но, как вы узнали, в данном случае подход с предвзятым отношением является чрезмерным.

Теперь об этом:

[A-z0-9 =]

То, что A-z - это либо опечатка, либо ошибка. Если вы ожидаете, что он будет соответствовать всем заглавным и строчным буквам, ну, это так. Но это также соответствует

'[', ']', '^', '_', '`` and '\'

... потому что их кодовые точки лежат между символами заглавных и строчных букв. Буквы ASCII, то есть.

...