<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><blockquote type="cite"><font color="#000000"><span style="background-color: rgba(255, 255, 255, 0);"><br>V mém případě může část dokumentu vypadat třeba takto:<br><br>   text text<br>   <verse/><br>   textB textB <czap> textC textC <verse/> textD textD </czap></span></font></blockquote><div id="AppleMailSignature"><br></div><br><div>Na tohle se vykašli. Jakmile dokument nemá stromovou strukturu, tak je vše tisíckrát složitější. Dikybohu toto XML neumí. </div><div><br></div><div>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?</div><div><br></div><div><verse>a b <mark sID="x"> c d</verse><verse>e f<mark eID="x"> g h</verse></div><div><br></div><div>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).</div><br>Petr Messner</div><div><br>27. 8. 2016 v 23:09, Matěj Cepl <<a href="mailto:mcepl@cepl.eu">mcepl@cepl.eu</a>>:<br><br></div><blockquote type="cite"><div><span>Dobrý den,</span><br><span></span><br><span>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 <a href="https://matej.ceplovi.cz/blog/parsing-milestoned-xml-in-python.html">https://matej.ceplovi.cz/blog/parsing-milestoned-xml-in-python.html</a>, 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).</span><br><span></span><br><span>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:</span><br><span></span><br><span>     <book></span><br><span>     <chapter></span><br><span>     <verse>text</verse></span><br><span>     ...</span><br><span>     </chapter></span><br><span>     ...</span><br><span>     </book></span><br><span></span><br><span>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):</span><br><span></span><br><span>     <book></span><br><span>     <chapter n="1" /></span><br><span>     <verse sID="ID1.1" />text of verse 1.1</span><br><span>     <verse eID="ID1.1" /> ....</span><br><span>     </book></span><br><span></span><br><span>V mém případě může část dokumentu vypadat třeba takto:</span><br><span></span><br><span>    text text</span><br><span>    <verse/></span><br><span>    textB textB <czap> textC textC <verse/> textD textD </czap></span><br><span></span><br><span>A chtěl bych aby moje knihovna naparsovala toto XML a vyprodukovala takovýto seznam:</span><br><span></span><br><span>    [(1, 1, "text text", ['text text']),</span><br><span>     (1, 2, "textB textB textC textC",</span><br><span>      ['<verse/>', 'textB textB', '<czap>', 'textC textC']),</span><br><span>     (1, 3, "textD textD", ['<verse/>', 'textD textD', '</czap>'])]</span><br><span></span><br><span>(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:</span><br><span></span><br><span>    if __name__ == '__main__':</span><br><span>        xml_file = ET.parse('tests/data/Mat-old.xml')</span><br><span>        parser = ET.XMLParser(target=ET.TreeBuilder())</span><br><span></span><br><span>        with open('test.txt', 'w', newline='\r\n') as out_txt, \</span><br><span>                open('test.xml', 'w', newline='\r\n') as out_xml:</span><br><span>            for ch, v, verse_txt, verse_xml in recursive_parse(xml_file):</span><br><span>                print(verse_txt, file=out_txt)</span><br><span>                # or directly parser.feed(verse_xml)</span><br><span>                # if verse_xml is not a list</span><br><span>                parser.feed(''.join(verse_xml))</span><br><span></span><br><span>            print(ET.tostring(parser.close(), encoding='unicode'),</span><br><span>                  file=out_xml)</span><br><span></span><br><span>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ů):</span><br><span></span><br><span>    def __iter__(self) -> Tuple[CollectedInfo, str]:</span><br><span>        """</span><br><span>        iterate through the first level elements</span><br><span>        """</span><br><span>        cur_chapter = 0</span><br><span>        cur_verse = 0</span><br><span>        collected_txt = ''</span><br><span>        # collected XML is NOT directly convertable into Element objects,</span><br><span>        # it should be treated more like a list of SAX-like events.</span><br><span>        #</span><br><span>        # xml.etree.ElementTree.fromstringlist(sequence, parser=None)</span><br><span>        # Parses an XML document from a sequence of string fragments.</span><br><span>        # sequence is a list or other sequence containing XML data fragments.</span><br><span>        # parser is an optional parser instance. If not given, the standard</span><br><span>        # XMLParser parser is used. Returns an Element instance.</span><br><span>        #</span><br><span>        # sequence = ["<html><body>", "text</bo", "dy></html>"]</span><br><span>        # element = ET.fromstringlist(sequence)</span><br><span>        # self.assertEqual(ET.tostring(element),</span><br><span>        #         b'<html><body>text</body></html>')</span><br><span></span><br><span>        for child in self.root.iter():</span><br><span>            if child.tag in ['titulek']:</span><br><span>                collected_txt += '\n{}\n'.format(child.text)</span><br><span>                collected_txt += child.tail or ''</span><br><span>            if child.tag in ['kap', 'vers']:</span><br><span>                if collected_txt and collected_txt.strip():</span><br><span>                    yield CollectedInfo(cur_chapter, cur_verse,</span><br><span>                                        re.sub(r'[\s\n]+', ' ', collected_txt,</span><br><span>                                               flags=re.DOTALL).strip()), \</span><br><span>                        child.tail or ''</span><br><span></span><br><span>                if child.tag == 'kap':</span><br><span>                    cur_chapter = int(child.get('n'))</span><br><span>                elif child.tag == 'vers':</span><br><span>                    cur_verse = int(child.get('n'))</span><br><span>            else:</span><br><span>                collected_txt += child.text or ''</span><br><span></span><br><span>                for sub_child in child:</span><br><span>                    for sub_info, sub_tail in MilestonedElement(sub_child):</span><br><span>                        if sub_info.verse == 0 or sub_info.chap == 0:</span><br><span>                            collected_txt += sub_info.text + sub_tail</span><br><span>                        else:</span><br><span>                            # FIXME what happens if sub_element contains</span><br><span>                            # multiple <verse/> elements?</span><br><span>                            yield CollectedInfo(</span><br><span>                                sub_info.chap, sub_info.verse,</span><br><span>                                collected_txt + sub_info.text), ''</span><br><span>                            collected_txt = sub_tail</span><br><span></span><br><span>                collected_txt += child.tail or ''</span><br><span></span><br><span>        yield CollectedInfo(0, 0, collected_txt), ''</span><br><span></span><br><span>Připadá Vám to jako způsobilá metoda jak dělat rekurzivní generátor nebo jsem něco nepochopil?</span><br><span></span><br><span>Děkuji za jakoukoli pomoc s tímto velmi zmateným úkolem.</span><br><span></span><br><span>Matěj</span><br><span></span><br><span>-- </span><br><span><a href="https://matej.ceplovi.cz/blog/">https://matej.ceplovi.cz/blog/</a>, Jabber: <a href="mailto:mcepl@ceplovi.cz">mcepl@ceplovi.cz</a></span><br><span>GPG Finger: 3C76 A027 CA45 AD70 98B5  BC1D 7920 5802 880B C9D8</span><br><span></span><br><span>He uses statistics as a drunken man uses lamp-posts... for</span><br><span>support, rather than illumination.</span><br><span>      -- Andrew Lang</span><br><span>_______________________________________________</span><br><span>Python mailing list</span><br><span><a href="mailto:python@py.cz">python@py.cz</a></span><br><span><a href="http://www.py.cz/mailman/listinfo/python">http://www.py.cz/mailman/listinfo/python</a></span><br><span></span><br><span>Visit: <a href="http://www.py.cz">http://www.py.cz</a></span><br></div></blockquote></body></html>