Некоторое время назад в этом блоге писали кое-что о регулярных экспрессах . Пока это еще не закончено, пример с мини-регулярным выражением — ничто не разрушает землю, но полезная техника, если вы еще этого не видели.
На примере реального мира часто упускается из виду особенность большинства механизмов регулярных выражений, заключающаяся в том, что подшаблоны могут быть полезны для относительно простого запуска токенизаторов . Проблема? Мне нужно было сопоставить слово любому из слов «Кантон», «Регион» или «Группа» в строке и выполнить последующее действие в зависимости от того, какое из них соответствует.
Имея дело с четырьмя основными языками в Швейцарии (немецким, французским, итальянским и английским), становится немного интереснее; «Кантон» переводится как «Кантон» на немецком языке и «Кантон» на итальянском языке, а «Регион» — «Regione» на итальянском языке. и группа — «Группа», «Группа» и «Группа» на немецком, французском и итальянском языках соответственно. Сложив их в три простых регулярных выражения, которые я имею;
- Кантон:
/cantone?|kanton/i
Кантон/cantone?|kanton/i
- Регион:
/regione?/i
- Группа:
/groupe?|grupp(?:o|e)/i
Теперь, изучив некоторую входную строку, я мог бы попробовать проверить каждое из этих регулярных выражений отдельно от строки, но это а) неэффективно и б) может привести к увеличению длины кода. Вместо этого я делаю одно регулярное выражение, используя подэлементы: /(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))/i
… затем выясняю, какой подэлемент соответствует после совпадения сделан.
Обратите внимание, что технически эта проблема на самом деле не является проблемой токенизации, а скорее просто классифицирует входные данные с общим именем, но метод может быть довольно легко расширен. В PHP решение предоставлено, например, третьим аргументом preg_match () ;
$inputs = array( 'Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe'); foreach ( $inputs as $input ) { preg_match("/(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))/i", $input, $matches); print_r($matches); }
$inputs = array( 'Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe'); foreach ( $inputs as $input ) { preg_match("/(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))/i", $input, $matches); print_r($matches); }
Я получаю вывод, как;
Array ( [0] => Kanton [1] => Kanton ) Array ( [0] => Regione [1] => [2] => Regione ) Array ( [0] => Gruppe [1] => [2] => [3] => Gruppe )
Обратите внимание, как первый элемент этого массива всегда совпадает с тем, что элементы с индексом 1+ соответствуют позиции подшаблона, с которым я сопоставляюсь, слева направо в шаблоне — это я могу использовать, чтобы сказать мне, что я на самом деле сопоставил, например;
$inputs = array( 'Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe'); $tokens = array('canton','region','group'); // the token names foreach ( $inputs as $input ) { if ( preg_match("/(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))/i", $input, $matches) ) { foreach ( array_keys( $matches) as $key) { if ( $key == 0 ) { continue; } // skip the first element // Look for the subpattern we matched... if ( $matches[$key] != "" ) { printf("Input: '%s', Token: '%s'n", $input, $tokens[$key-1]); } } } }
$inputs = array( 'Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe'); $tokens = array('canton','region','group'); // the token names foreach ( $inputs as $input ) { if ( preg_match("/(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))/i", $input, $matches) ) { foreach ( array_keys( $matches) as $key) { if ( $key == 0 ) { continue; } // skip the first element // Look for the subpattern we matched... if ( $matches[$key] != "" ) { printf("Input: '%s', Token: '%s'n", $input, $tokens[$key-1]); } } } }
Что дает мне вывод, как;
Input: 'Kanton Zuerich', Token: 'canton' Input: 'Frauenfeld Regione', Token: 'region' Input: 'Fricktal Gruppe', Token: 'group'
… Так что теперь я могу классифицировать входные данные для одного из известных токенов и реагировать соответствующим образом. Большинство регулярных выражений. apis предоставляет что-то в этом роде, например, здесь то же самое (и намного чище) в Python, который я и использовал в этой проблеме;
import re p = re.compile('(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))', re.I) inputs = ('Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe') tokens = ('canton','region','group') for input in inputs: m = p.search(input) if not m: continue for group, token in zip(m.groups(), tokens): if group is not None: print "Input: '%s', Token: '%s'" % ( input, token )
import re p = re.compile('(cantone?|kanton)|(regione?)|(groupe?|grupp(?:o|e))', re.I) inputs = ('Kanton Zuerich', 'Frauenfeld Regione', 'Fricktal Gruppe') tokens = ('canton','region','group') for input in inputs: m = p.search(input) if not m: continue for group, token in zip(m.groups(), tokens): if group is not None: print "Input: '%s', Token: '%s'" % ( input, token )
Может быть уменьшено в дальнейшем с использованием списочных представлений, но не думаю, что это помогает читабельности в этом случае.
Альтернативная проблема, чтобы дать вам понять, как этот метод может быть применен. Допустим, вы хотите проанализировать HTML-документ и перечислить подмножество уровня блока против встроенных тегов уровня, которые он содержит. Вы можете сделать это с двумя подэлементами, например (</?(?:div|h[1-6]{1}|p|ol|ul|pre).*?>)|(</?(?:span|code|em|strong|a).*?>)
(обратите внимание, что это регулярное выражение «как есть» связано с идеей жадности в Python — вам нужно изменить его на PHP), что приводит к чему-то вроде этого — python;
p = re.compile('(</?(?:div|h[1-6]{1}|p|ol|ul|pre).*?>)|(</?(?:span|code|em|strong|a).*?>)') for match in p.finditer('foo <div> test <strong>bar</strong> test 1</div> bar'): print "[pos: %s] matched %s" % ( match.start(), str(match.groups()) )
p = re.compile('(</?(?:div|h[1-6]{1}|p|ol|ul|pre).*?>)|(</?(?:span|code|em|strong|a).*?>)') for match in p.finditer('foo <div> test <strong>bar</strong> test 1</div> bar'): print "[pos: %s] matched %s" % ( match.start(), str(match.groups()) )
Вызов match.groups()
возвращает кортеж, который сообщает вам, какой match.start()
совпал, в то время как match.start()
сообщает вам позицию символа в документе, где было match.start()
совпадение, позволяя вам извлечь подстроки из документа.