Próbuję parsować słowa, które mogą być podzielone na wiele linii z kombinacji backslash-newline ("\\n
") przy użyciu pyparsing. Oto co zrobiłem:Korzystanie z pyparsing do parsowania wyrazu escape-split na wiele linii
from pyparsing import *
continued_ending = Literal('\\') + lineEnd
word = Word(alphas)
split_word = word + Suppress(continued_ending)
multi_line_word = Forward()
multi_line_word << (word | (split_word + multi_line_word))
print multi_line_word.parseString(
'''super\\
cali\\
fragi\\
listic''')
Wyjście mogę to ['super']
, zaś oczekiwany wynik jest ['super', 'cali', fragi', 'listic']
. Jeszcze lepiej byłoby je wszystkie dołączył jako jedno słowo (które myślę, że mogę po prostu zrobić z multi_line_word.parseAction(lambda t: ''.join(t))
.
Próbowałem patrząc na ten kod w pyparsing helper, ale daje mi błąd, maximum recursion depth exceeded
.
EDIT 2009-11-15: Uświadomiłem sobie później, że pyparsing robi się trochę hojny w odniesieniu do białej przestrzeni, a to prowadzi do pewnych złych założeń, że to, o czym myślałem, że analizowałem było dużo luźniejsze. nie widzę żadnej białej przerwy między częścią słowa, ucieczką i znakiem EOL. mały przykładowy ciąg powyżej jest niewystarczający jako przypadek testowy, dlatego napisałem następujące testy jednostkowe. Kod, który przejdzie te testy, powinien być w stanie dopasować to, co intuicyjnie uważam za słowo "ucieczkę-split" — i tylko słowo "ucieczka". Nie pasują do podstawowego słowa, które nie jest podzielone przez ucieczkę. Możemy: — i uważam, że powinienem użyć innej konstrukcji gramatycznej. Utrzymuje to wszystko w porządku, mając dwie oddzielne.
import unittest
import pyparsing
# Assumes you named your module 'multiline.py'
import multiline
class MultiLineTests(unittest.TestCase):
def test_continued_ending(self):
case = '\\\n'
expected = ['\\', '\n']
result = multiline.continued_ending.parseString(case).asList()
self.assertEqual(result, expected)
def test_continued_ending_space_between_parse_error(self):
case = '\\ \n'
self.assertRaises(
pyparsing.ParseException,
multiline.continued_ending.parseString,
case
)
def test_split_word(self):
cases = ('shiny\\', 'shiny\\\n', ' shiny\\')
expected = ['shiny']
for case in cases:
result = multiline.split_word.parseString(case).asList()
self.assertEqual(result, expected)
def test_split_word_no_escape_parse_error(self):
case = 'shiny'
self.assertRaises(
pyparsing.ParseException,
multiline.split_word.parseString,
case
)
def test_split_word_space_parse_error(self):
cases = ('shiny \\', 'shiny\r\\', 'shiny\t\\', 'shiny\\ ')
for case in cases:
self.assertRaises(
pyparsing.ParseException,
multiline.split_word.parseString,
case
)
def test_multi_line_word(self):
cases = (
'shiny\\',
'shi\\\nny',
'sh\\\ni\\\nny\\\n',
' shi\\\nny\\',
'shi\\\nny '
'shi\\\nny captain'
)
expected = ['shiny']
for case in cases:
result = multiline.multi_line_word.parseString(case).asList()
self.assertEqual(result, expected)
def test_multi_line_word_spaces_parse_error(self):
cases = (
'shi \\\nny',
'shi\\ \nny',
'sh\\\n iny',
'shi\\\n\tny',
)
for case in cases:
self.assertRaises(
pyparsing.ParseException,
multiline.multi_line_word.parseString,
case
)
if __name__ == '__main__':
unittest.main()
Użycie 'Combine' również nie wymusza żadnych odstępów. – PaulMcG
Interesujące. wypróbowany 'multi_line_word = Combine (Combine (OneOrMore (split_word)) + Opcjonalne (słowo))' , ale łamie się w przypadku '' sh \\\ nin '', ponieważ nie powoduje wyjątku, ale zamiast tego zwraca '['sh']'. Czy czegoś brakuje? – gotgenes
Cóż, twoje słowo to nie tylko litery rozciągające się na "\" - znak nowej linii, ale jest tam to miejsce przed literą "i", która liczy się jako podział słów, więc połączenie zatrzymuje się po "sh". Możesz * modyfikować * Kombinuj z sąsiednim argumentem = False konstruktor, ale uważaj - możesz skończyć, wysysając cały plik jako pojedyncze słowo! Możesz też ponownie zdefiniować definicję kontynuacji, aby uwzględnić wszystkie spacje po linii, jeśli chcesz także zwinąć wszystkie spacje wiodące. – PaulMcG