Лексемы в регулярных выражениях

Введение

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

Каждой лексеме в выражении присваивается число, начиная с 1, идущее слева направо. Чтобы сделать ссылку на лексему позже в выражении, обратитесь к нему с помощью обратной косой черты, за которой следует номер маркера. Для примера при ссылке на лексему, сгенерированную третьим набором круглых скобок в выражении, используйте \3.

В качестве простого примера, если вы хотели искать одинаковые последовательные буквы в символьном массиве, можно было захватить первую букву как лексему, а затем сразу после этого искать соответствующий символ. В выражении, показанном ниже, (\S) фраза создает лексему каждый раз, когда regexp соответствует любому непроницаемому символу в символьном массиве. Вторая часть выражения, '\1', ищет второй образец того же символа сразу после первого.

poe = ['While I nodded, nearly napping, ' ...
       'suddenly there came a tapping,'];

[mat,tok,ext] = regexp(poe, '(\S)\1', 'match', ...
               'tokens', 'tokenExtents');
mat
mat =

  1×4 cell array

    {'dd'}    {'pp'}    {'dd'}    {'pp'}

Массив ячеек tok содержит массивы ячеек, каждый из которых содержит лексему.

tok{:}
ans =

  1×1 cell array

    {'d'}


ans =

  1×1 cell array

    {'p'}


ans =

  1×1 cell array

    {'d'}


ans =

  1×1 cell array

    {'p'}

Массив ячеек ext содержит числовые массивы, каждый из которых содержит начальный и конечный индексы для лексемы.

ext{:}
ans =

    11    11


ans =

    26    26


ans =

    35    35


ans =

    57    57

В другом примере захватывайте пары совпадающего HTML (например <a> и </a>) и текст между ними. Выражение, используемое в этом примере,

expr = '<(\w+).*?>.*?</\1>';

Первая часть выражения, '<(\w+)', соответствует угловой скобке открытия (<), за которым следует один или несколько буквенных, числовых или символов подчеркивания. Вложенные круглые скобки захватывают символы маркера, следующие за угловой скобкой открытия.

Вторая часть выражения, '.*?>.*?', соответствует оставшейся части этого HTML (символы до >) и любые символы, которые могут предшествовать следующей угловой скобке открытия.

Последняя часть, '</\1>', соответствует всем символам в окончательном HTML. Этот тег состоит из последовательности </tag>, где tag - все символы, захваченные в качестве лексемы.

hstr = '<!comment><a name="752507"></a><b>Default</b><br>';
expr = '<(\w+).*?>.*?</\1>';

[mat,tok] = regexp(hstr, expr, 'match', 'tokens');
mat{:}
ans =

    '<a name="752507"></a>'


ans =

    '<b>Default</b>'
tok{:}
ans =

  1×1 cell array

    {'a'}


ans =

  1×1 cell array

    {'b'}

Несколько лексемы

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

andy ted bob jim andrew andy ted mark

Вы принимаете решение искать вышеуказанный текст со следующим шаблоном поиска:

and(y|rew)|(t)e(d)

Этот шаблон имеет три круглых выражения, которые генерируют лексемы. Когда вы наконец-то выполняете поиск, для каждого соответствия генерируются следующие лексемы.

Матч

Лексема 1

Лексема 2

andy

y

 

ted

t

d

andrew

rew

 

andy

y

 

ted

t

d

Используются только круглые скобки самого высокого уровня. Для примера, если шаблон поиска and(y|rew) находит текст andrew, лексеме 1 присваивается значение rew. Однако, если шаблон поиска (and(y|rew)) используется, лексеме 1 присваивается значение andrew.

НеСопоставленные лексемы

Для тех лексем, заданных в регулярном выражении, которые не совпадают в просчитываемом тексте, regexp и regexpi возвращает пустой символьный вектор ('') в качестве выхода лексемы и степени, обозначающей положение в строке, в которой ожидался маркер.

Пример, показанный здесь, выполняет regexp для вектора символов, задающего путь, возвращенный из MATLAB® tempdir функция. Регулярное выражение expr включает шесть спецификаторов маркеров, по одному для каждого участка пути. Третий спецификатор [a-z]+ не имеет никакого соответствия в векторе символов, потому что эта часть пути, Profiles, начинается с заглавной буквы:

chr = tempdir
chr =

    'C:\WINNT\Profiles\bpascal\LOCALS~1\Temp\'
expr = ['([A-Z]:)\\(WINNT)\\([a-z]+)?.*\\' ...
        '([a-z]+)\\([A-Z]+~\d)\\(Temp)\\'];

[tok, ext] = regexp(chr, expr, 'tokens', 'tokenExtents');

Когда лексема не найдена в тексте, regexp возвращает пустой символьный вектор ('') как лексема и числовой массив с расширением маркера. Первое число экстента является строковым индексом, который помечает, где ожидался маркер, а второе число экстента равно единице меньше первой.

В случае с этим примером пустая лексема является третьим, заданным в выражении, поэтому третий возвращённая лексема пуста:

tok{:}
ans =

  1×6 cell array

    {'C:'}    {'WINNT'}    {0×0 char}    {'bpascal'}    {'LOCALS~1'}    {'Temp'}

Третий экстент токена, возвращенный в переменной ext имеет начальный индекс, равный 10, где не совпадающий термин, Profiles, начинается в пути. Индекс конечного экстента устанавливается на единицу меньше, чем начальный индекс, или на 9:

ext{:}
ans =

     1     2
     4     8
    10     9
    19    25
    27    34
    36    39

Лексемы в замещаемом тексте

При использовании лексем в тексте замены ссылаться на них используя $1, $2, и т.д. вместо \1, \2, и т.д. Этот пример захватывает две лексемы и отменяет их порядок. Первый, $1, есть 'Norma Jean' и второй, $2, есть 'Baker'. Обратите внимание, что regexprep возвращает измененный текст, а не вектор начальных индексов.

regexprep('Norma Jean Baker', '(\w+\s\w+)\s(\w+)', '$2, $1')
ans =

    'Baker, Norma Jean'

Захват имен

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

При ссылке на именованную лексему в выражении используйте синтаксис \k<name> вместо числового \1, \2, и т.д.:

poe = ['While I nodded, nearly napping, ' ...
       'suddenly there came a tapping,'];

regexp(poe, '(?<anychar>.)\k<anychar>', 'match')
ans =

  1×4 cell array

    {'dd'}    {'pp'}    {'dd'}    {'pp'}

Именованные лексемы также могут быть полезны при маркировке выхода из функций регулярного выражения MATLAB. Это особенно верно, когда вы обрабатываете много частей текста.

Для примера проанализируйте различные части уличных адресов из нескольких векторов символов. Каждой лексеме в выражении присваивается краткое имя:

chr1 = '134 Main Street, Boulder, CO, 14923';
chr2 = '26 Walnut Road, Topeka, KA, 25384';
chr3 = '847 Industrial Drive, Elizabeth, NJ, 73548';

p1 = '(?<adrs>\d+\s\S+\s(Road|Street|Avenue|Drive))';
p2 = '(?<city>[A-Z][a-z]+)';
p3 = '(?<state>[A-Z]{2})';
p4 = '(?<zip>\d{5})';

expr = [p1 ', ' p2 ', ' p3 ', ' p4];

Как показывают следующие результаты, можно упростить работу с выходом при помощи именованных лексем:

loc1 = regexp(chr1, expr, 'names')
loc1 = 

  struct with fields:

     adrs: '134 Main Street'
     city: 'Boulder'
    state: 'CO'
      zip: '14923'
loc2 = regexp(chr2, expr, 'names')
loc2 = 

  struct with fields:

     adrs: '26 Walnut Road'
     city: 'Topeka'
    state: 'KA'
      zip: '25384'
loc3 = regexp(chr3, expr, 'names')
loc3 = 

  struct with fields:

     adrs: '847 Industrial Drive'
     city: 'Elizabeth'
    state: 'NJ'
      zip: '73548'

См. также

| |

Похожие темы