Dobrý den, zkouším napsat v Pythonu 3.4+ nástroj pracující s milestonovanými XML soubory (zdrojové texty překladu Bible). Podrobně jsem to popsal v blogpostu https://matej.ceplovi.cz/blog/parsing-milestoned-xml-in-python.html, zde jenom velice stručně. Milestones (milníky?) jsou metoda jak obejít neschopnost XML pracovat s několika překrývajícími se hierarchiemi v jednom souboru. Tak třeba právě v biblických textech (TEI se potýká s podobnými problémy) je základní struktura kniha-kapitola-verš, ale přes to jsou další elementy které se překrývají. Několik veršů (nebo jejich částí) jsou sdruženy do logických oddílů (ale některé začínají v půli verše a často přesahují hranice kapitol), nebo třeba zejména anglické biblické překlady mají v oblibě značit výroky Pána Ježíše zvlášť (což pochopitelně často začíná a končí v půli verše).
Jedna z metod (používaná husta právě v biblických textech, TEI a podobných složitě strukturovaných dokumentech) jsou právě milníky. Takže místo aby byl text značen nějak takto: <book> <chapter> <verse>text</verse> ... </chapter> ... </book> použijí se na hranici knihy, kapitoly i verše milníky takto (buď se zvláštním označením konce veršů nebo taky ne): <book> <chapter n="1" /> <verse sID="ID1.1" />text of verse 1.1 <verse eID="ID1.1" /> .... </book> V mém případě může část dokumentu vypadat třeba takto: text text <verse/> textB textB <czap> textC textC <verse/> textD textD </czap> A chtěl bych aby moje knihovna naparsovala toto XML a vyprodukovala takovýto seznam: [(1, 1, "text text", ['text text']), (1, 2, "textB textB textC textC", ['<verse/>', 'textB textB', '<czap>', 'textC textC']), (1, 3, "textD textD", ['<verse/>', 'textD textD', '</czap>'])] (první dvě čísla jsou číslo kapitoly a verše, poslední položka tuplu je seznam kousků XML v podobě přijatelné pro ElementTree.fromstringlist). Takže představoval bych si generátor, který by byl schopen takovéto API: if __name__ == '__main__': xml_file = ET.parse('tests/data/Mat-old.xml') parser = ET.XMLParser(target=ET.TreeBuilder()) with open('test.txt', 'w', newline='\r\n') as out_txt, \ open('test.xml', 'w', newline='\r\n') as out_xml: for ch, v, verse_txt, verse_xml in recursive_parse(xml_file): print(verse_txt, file=out_txt) # or directly parser.feed(verse_xml) # if verse_xml is not a list parser.feed(''.join(verse_xml)) print(ET.tostring(parser.close(), encoding='unicode'), file=out_xml) Po různých peripetiích (popsaných v tom zmiňovaném blogpostu) jsem v současném okamžiku u tohoto (zatím bez shromažďování XML kousků): def __iter__(self) -> Tuple[CollectedInfo, str]: """ iterate through the first level elements """ cur_chapter = 0 cur_verse = 0 collected_txt = '' # collected XML is NOT directly convertable into Element objects, # it should be treated more like a list of SAX-like events. # # xml.etree.ElementTree.fromstringlist(sequence, parser=None) # Parses an XML document from a sequence of string fragments. # sequence is a list or other sequence containing XML data fragments. # parser is an optional parser instance. If not given, the standard # XMLParser parser is used. Returns an Element instance. # # sequence = ["<html><body>", "text</bo", "dy></html>"] # element = ET.fromstringlist(sequence) # self.assertEqual(ET.tostring(element), # b'<html><body>text</body></html>') for child in self.root.iter(): if child.tag in ['titulek']: collected_txt += '\n{}\n'.format(child.text) collected_txt += child.tail or '' if child.tag in ['kap', 'vers']: if collected_txt and collected_txt.strip(): yield CollectedInfo(cur_chapter, cur_verse, re.sub(r'[\s\n]+', ' ', collected_txt, flags=re.DOTALL).strip()), \ child.tail or '' if child.tag == 'kap': cur_chapter = int(child.get('n')) elif child.tag == 'vers': cur_verse = int(child.get('n')) else: collected_txt += child.text or '' for sub_child in child: for sub_info, sub_tail in MilestonedElement(sub_child): if sub_info.verse == 0 or sub_info.chap == 0: collected_txt += sub_info.text + sub_tail else: # FIXME what happens if sub_element contains # multiple <verse/> elements? yield CollectedInfo( sub_info.chap, sub_info.verse, collected_txt + sub_info.text), '' collected_txt = sub_tail collected_txt += child.tail or '' yield CollectedInfo(0, 0, collected_txt), '' Připadá Vám to jako způsobilá metoda jak dělat rekurzivní generátor nebo jsem něco nepochopil? Děkuji za jakoukoli pomoc s tímto velmi zmateným úkolem. Matěj -- https://matej.ceplovi.cz/blog/, Jabber: mc...@ceplovi.cz GPG Finger: 3C76 A027 CA45 AD70 98B5 BC1D 7920 5802 880B C9D8 He uses statistics as a drunken man uses lamp-posts... for support, rather than illumination. -- Andrew Lang _______________________________________________ Python mailing list python@py.cz http://www.py.cz/mailman/listinfo/python Visit: http://www.py.cz