Можно ли сделать re.finditer () исключающим разделители из групп? - PullRequest
1 голос
/ 10 ноября 2019

Моя задача - преобразовать «исходный» текст в последовательность элементов: TEXT и INPUT. INPUT - это те части, которые обернуты в две звездочки, а TEXT - все остальное.

Вот пример:

>>> source = 'I came *across* these old photos when I *was* tidying the closet.'
>>> parse(source)
TEXT: 'I came '
INPUT: 'across'
TEXT: ' these old photos when I '
INPUT: 'was'
TEXT: ' tidying the closet.'

Цель такого анализа - создать «заполнение»интерактивный инструмент для языковой подготовки. Проанализированные элементы в конечном итоге перейдут на клиентскую сторону, где элементы TEXT отображаются «как есть», а элементы INPUT отображаются как поля ввода для ввода пользователем.

Для этого янемного изменили пример, приведенный в Запись токенизатора в документации библиотеки re. Вот мое решение:

def parse(text):
    token_specifications = [
        ('INPUT', r'(\*\w\*)|(\*\w+[^*]*\w+\*)'),
        ('TEXT', r'[^*]+'),
    ]
    token_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specifications)
    elements = []
    for mo in re.finditer(token_regex, text):
        kind = mo.lastgroup
        value = mo.group()
        # A hack to remove the delimiters
        if kind == 'INPUT':
            value = value.replace('*', '')
        print("%s: '%s'" % (kind, value))
        elements.append((kind, value))
    # Testing the result
    if elements != [
        ('INPUT', 'This'),
        ('TEXT', ' is '),
        ('INPUT', 'a'),
        ('TEXT', ' text that '),
        ('INPUT', 'needs to be'),
        ('TEXT', ' parsed. '),
        ('INPUT', 'Highlighted'),
        ('TEXT', ' elements must be in '),
        ('INPUT', 'INPUT'),
        ('TEXT', ' group.'),
    ]:
        raise Exception("Parsing result is wrong!")

text = '*This* is *a* text that *needs to be* parsed. *Highlighted* elements must be in *INPUT* group.'
parse(text)

Это работает, как и ожидалось, и выглядит достаточно аккуратно, за исключением одной небольшой проблемы. А именно, элементы INPUT поставляются вместе со звездочками, и я должен явно удалить их (см. A hack to remove the delimiters часть кода).

Есть ли способ заставить функцию finditer() выдавать разделителипрочь, так что мне не нужно делать это явно?

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

1 Ответ

1 голос
/ 10 ноября 2019

Вы могли бы упростить свои REGEX и свои code:

text = '*This* is *a* text that *needs to be* parsed. *Highlighted* elements must be in *INPUT* group.'
elements = []
token_specifications = [
    ('INPUT', r'\*(?P<{}>\w+(?:[^*]\w+)*)\*'),
    ('TEXT', r'(?P<{}>[^*]+)'),
    ]
token_regex = '|'.join(exp.format(k) for k, exp in token_specifications)
# for match in re.finditer(r'\*(?P<INPUT>[^*]+)\*|(?P<TEXT>[^*]+)', text):
for match in re.finditer(token_regex, text):
    kind = match.lastgroup
    value = match.group(kind)
    elements.append((kind, value))

print(elements)
# [('INPUT', 'This'), ('TEXT', ' is '), ('INPUT', 'a'), ('TEXT', ' text that '), ('INPUT', 'needs to be'),
# ('TEXT', ' parsed. '), ('INPUT', 'Highlighted'), ('TEXT', ' elements must be in '), ('INPUT', 'INPUT'), ('TEXT', ' group.')]

Я объединил ваше INPUT регулярное выражение, подобное этому \*(\w+(?:[^*]*\w+)*)\*, с одной группой захвата, которая не включает *. и вы получите захваченную группу по имени, например: match.group('NAME_OF_GROUP')

...