> > 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>
Na tohle se vykašli. Jakmile dokument nemá stromovou strukturu, tak je vše tisíckrát složitější. Dikybohu toto XML neumí. Co je špatného na tom příkladě se sID/eID? Jestli je to v tom, že to neoznačuje i část obsahu/verše, co takhle tohle? <verse>a b <mark sID="x"> c d</verse><verse>e f<mark eID="x"> g h</verse> Na pochopení toho kódu bych si musel sednout někam v klidu, ale zatím jen taková drobnost - nedělej skládání řetězců stylem txt += part, je to strašně pomalé (O(n^2) místo O(n)).. Z txt si udělej list a používej append: txt.append(part) a na konci "".join(txt). Petr Messner 27. 8. 2016 v 23:09, Matěj Cepl <mc...@cepl.eu>: > 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
_______________________________________________ Python mailing list python@py.cz http://www.py.cz/mailman/listinfo/python Visit: http://www.py.cz