Как проанализировать текстовый файл и извлечь различное количество совпадений в строке?

#python #regex #parsing

#python #регулярное выражение #синтаксический анализ

Вопрос:

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

Мой документ в основном содержит несколько таблиц, которые всегда структурированы следующим образом:

 Report Title



  AutoChem II 2920 V5.03    Unit  1    Serial # 937    Page 1  

     Sample:    SAMPLE_NAME    
   Operator:    Jane Doe                              
  Submitter:    ABCD                              
       File:    FILE_PATH 

    Started:    20.03.2020 8:17:56     Sample Mass:      0.4639 g           
  Completed:    20.03.2020 23:01:48    Report Time:    24.03.2020 12:18:36  

Comments: Comment





TCD Signal (a.u.) vs. Temperature


TCD Signal (a.u.) - NO2 TPD, 650C, 1.5h ads, 1.h flush, TPD He
Temperature (°C)    TCD Signal (a.u.)
120                  -0
120.024              0.000154972
120.028              -5.48363e-005
120.014              0.000126362
120.036              9.53674e-005
...
  

Использование словаря регулярных выражений

 rx_dict = {
    'sample': re.compile(r'Sample: (?P<sample>.*)n'),
    'operator': re.compile(r'Operator: (?P<operator>.*)n'),
    'started': re.compile(r'Started: (?P<started>.*)n'),
    'comments': re.compile(r'Comments: (?P<comments>.*)n'),
}
  

и анализатор строк, подобный этому

 def _parse_line(line):
    """
    Do a regex search against all defined regexes and
    return the key and match result of the first matching regex

    """

    for key, rx in rx_dict.items():
        match = rx.search(line)
        if match:
            print(key)
            return key, match
    # if there are no matches
    return None, None
  

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

  1. Время начала также включает массу выборки. Я мог бы, конечно, разделить это впоследствии, но мне интересно, есть ли более элегантный способ сделать это.
  2. Я также хочу определить начало таблицы. Итак, в этом случае текущая _parse_line функция не работает, потому что она ожидает ключ и соответствующее выражение. Как я могу обойти это?

Комментарии:

1. Два вопроса: начинается ли каждая таблица с Report Title ? Каков именно ваш ожидаемый результат из примерной таблицы в вопросе?

Ответ №1:

Во-первых, вы можете быть немного более точными со всеми вашими регулярными выражениями, и тогда у вас не возникнет трудностей с сопоставлением только Started значения (см. Ниже). Кроме того, вы можете повысить эффективность, создав одно регулярное выражение и выполнив итеративный поиск:

 import re

report = """Report Title



  AutoChem II 2920 V5.03    Unit  1    Serial # 937    Page 1

     Sample:    SAMPLE_NAME
   Operator:    Jane Doe
  Submitter:    ABCD
       File:    FILE_PATH

    Started:    20.03.2020 8:17:56     Sample Mass:      0.4639 g
  Completed:    20.03.2020 23:01:48    Report Time:    24.03.2020 12:18:36

Comments: Comment
"""

# Not a dictionary!
# If you are stuck with a passed dictionary whose values are compiled regular expressions, then:
# rx_list = map(lambda v: v.pattern, rx_list.values())
rx_list = [
    r'Sample:s (?P<sample>.*)n',
    r'Operator:s (?P<operator>.*)n',
    r'Started:s (?P<started>d .d .d  d :d :d )',
    r'Comments:s (?P<comments>.*)s*'
]

regex = re.compile('|'.join(rx_list))
# each iteration just matches one element of the report
d = {}
for m in regex.finditer(report):
    key = m.lastgroup # this is the one and only group name matched
    value = m[key]
    print(key, '->', value) # you can add these key and values to a dictionary, if you wish:
    d[key] = value
print(d)
  

С принтами:

 sample -> SAMPLE_NAME
operator -> Jane Doe
started -> 20.03.2020 8:17:56
comments -> Comment
{'sample': 'SAMPLE_NAME', 'operator': 'Jane Doe', 'started': '20.03.2020 8:17:56', 'comments': 'Comment'}