Парсинг полуструктурированных текстовых строк в Python - PullRequest
0 голосов
/ 01 марта 2019

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

Некоторые примеры:

  1. На устройстве 1 Установите слова 45 и 46в шестнадцатеричное значение 331
  2. На устройстве 1 задайте слова 45 и 46 битов 3.,7–280
  3. на устройстве 1 установите слово 45 на октаву 332
  4. на устройстве 1 установите скоростьдо 60 узлов Слова 3-4 до гекса 34 (в исходном тексте используется много других способов)

Проблема в том, что она не всегда логична или непоследовательна

Я смотрел наРегулярное выражение и сопоставление определенных слов.Это работает хорошо, но когда мне нужно знать следующее слово (например, в «Word 24» я бы соответствовал «Word», затем попытался выяснить, является ли следующий токен числом или нет).В случае «Слова» мне нужно искать слова для установки, а также их значения.

в примере 1, он должен выдавать Set word 45 to hex 331 и Set word 46 to hex 331 или, если возможно, Set word 45 to hex 331 and word 46 to hex 331

я попытался использовать метод findall для re - это дало бы мне только совпадающие слова, а затем я должен попытаться найти следующее слово (то есть значение) вручную

в качестве альтернативы, яМожно разделить строку, используя пробел, и обработать каждое слово вручную, а затем сделать что-то вроде

, если предположить, что список равен

['On', 'device1:', 'set', 'Word', '1', '', 'to', '88', 'and', 'word', '2', 'to', '2151']

for i in range (0,sp.__len__()):
    rew = re.search("[Ww]ord", sp[i])
    if rew:
        print ("Found word, next val is ", sp[i+1])

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

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

В зависимости от того, что вы действительно хотите от этих строк, вы можете использовать парсер, например, parsimonious:

from parsimonious.nodes import NodeVisitor
from parsimonious.grammar import Grammar

grammar = Grammar(
    r"""
    command     = set operand to? number (operator number)* middle? to? numsys? number
    operand     = (~r"words?" / "speed") ws
    middle      = (~r"[Ww]ords" / "bits")+ ws number
    to          = ws "to" ws
    number      = ws ~r"[-\d.]+" "kts"? ws
    numsys      = ws ("oct" / "hex") ws
    operator    = ws "and" ws
    set         = ~"[Ss]et" ws
    ws          = ~r"\s*"
    """
)

class HorribleStuff(NodeVisitor):
    def __init__(self):
        self.cmds = []

    def generic_visit(self, node, visited_children):
        pass

    def visit_operand(self, node, visited_children):
        self.cmds.append(('operand', node.text))

    def visit_number(self, node, visited_children):
        self.cmds.append(('number', node.text))


examples = ['Set word 45 and 46 to hex 331',
            'set words 45 and 46 bits 3..7 to 280',
            'set word 45 to oct 332',
            'set speed to 60kts Words 3-4 to hex 34']


for example in examples:
    tree = grammar.parse(example)
    hs = HorribleStuff()
    hs.visit(tree)
    print(hs.cmds)

Это даст

[('operand', 'word '), ('number', '45 '), ('number', '46 '), ('number', '331')]
[('operand', 'words '), ('number', '45 '), ('number', '46 '), ('number', '3..7 '), ('number', '280')]
[('operand', 'word '), ('number', '45 '), ('number', '332')]
[('operand', 'speed '), ('number', '60kts '), ('number', '3-4 '), ('number', '34')]
0 голосов
/ 01 марта 2019

Я предлагаю вам разработать программу, которая постепенно исследует синтаксис, который люди использовали для написания сценариев.

Например, каждая инструкция в ваших примерах, похоже, разбивается на часть устройства и часть настроек,Таким образом, вы можете попробовать сопоставить каждую строку с регулярным выражением ^(.+) set (.+).Если вы найдете линии, которые не соответствуют этому шаблону, распечатайте их.Изучите выходные данные, найдите общий шаблон, соответствующий некоторым из них, добавьте соответствующее регулярное выражение в вашу программу (или измените существующее регулярное выражение) и повторите.Продолжайте до тех пор, пока вы не узнаете (в общих чертах) каждую строку ввода.

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

(Есливаш ввод содержит опечатки, например, кто-то написал «ste» для «set», тогда вы можете либо изменить регулярное выражение, чтобы учесть это (... (set|ste) ...), либо перейти (копию) входного файла и просто исправить опечатку.)

Затем вернитесь к строкам, которые совпадают с ^(.+) set (.+), распечатайте только первую группу для каждой и повторите описанный выше процесс только для этих подстрок.Затем повторите процедуру для второй группы в каждой инструкции «set».И так далее, рекурсивно.

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

В зависимости от вашего опыта работы с Python, вы можете найти способы сделать код кратким.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...