Как разобрать «здесь документ» в Python? - PullRequest
0 голосов
/ 09 мая 2019

Я хочу написать метод Python, который читает текстовый файл со значениями ключа:

FOO=BAR
BUZ=BLEH

Я также хочу поддерживать переводы строк через кавычки и \n, а также поддерживая here-docs :

MULTILINE1="This\nis a test"
MULTILINE2= <<DOC
This
is a test
DOC

Хотя первое легко реализовать, я борюсь со вторым. Может быть, есть что-то в stdlib Python (то есть shlex ), которое я уже могу использовать?

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Содержимое "test.txt":

FOO=BAR
BUZ=BLEH
MULTILINE1="This\nis a test"
MULTILINE2= <<DOC
This
is a test
DOC

Функция:

def read_strange_file(filename):
    with open(filename) as f:
        file_content = f.read().splitlines()

    res = {}
    key, value, delim = "", "", ""
    for line in file_content:
        if "=" in line and not delim:
            key, value = line.split("=")
            if value.strip(" ").startswith("<<"):
                delim = value.strip(" ")[2:] # extracting delimiter keyword
                value = ""
                continue
        if not delim or (delim and line == delim):
            if value.startswith("\"") and value.endswith("\""):
                # [1: -1] delete quotes
                value = bytes(value[1: -1], "utf-8").decode("unicode_escape") 
            if delim:
                value = value[:-1] # delete "\n"
            res[key] = value
            delim = ""
        if delim:
            value += line + "\n"

    return res

Usage:

result = read_strange_file("test.txt")
print(result)

Выход:

{'FOO': 'BAR', 'BUZ': 'BLEH', 'MULTILINE1': 'This\nis a test', 'MULTILINE2': 'This\nis a test'}
0 голосов
/ 09 мая 2019

Я предполагаю, что это тестовая строка (т.е. в конце каждой строки есть невидимые \n символы):

s = ''
s += 'MULTILINE1="This\nis a test"\n'
s += 'MULTILINE2= <<DOC\n'
s += 'This\n'
s += 'is a test\n'
s += 'DOC\n'

Лучшее, что я могу сделать, это обмануть с помощью NumPy:

import numpy as np

A  = np.asarray([ss.rsplit('\n', 1)  for ss in ('\n'+s).split('=')])
keys   = A[:-1,1].tolist()
values = A[1:,0].tolist()

#optionally parse here-documents
di     = 'DOC' #delimiting identifier
values = [v.strip().lstrip('<<%s\n'%di).rstrip('\n%s'%di) for v in values]

print('Keys: ', keys)
print('Values: ', values)

#if you want a dictionary:
d      = dict( zip(keys, values) )

В результате:

Keys:  ['MULTILINE1', 'MULTILINE2']
Values:  ['"This\nis a test"', '"This\nis a test"']

Он работает, незаметно добавляя символ \n в начало строки, затем разделяя всю строку на = символов, а затем, наконец, использует rsplit, чтобы сохранить все значения справа от =, даже когда эти значения содержат несколько \n символов. Печать массива A проясняет ситуацию:

[['',                             'MULTILINE1'],
 ['"This\nis a test"',            'MULTILINE2'],
 [' <<DOC\nThis\nis a test\nDOC', ''         ]]
...