txt nach xml mit Schema

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Montag 8. Januar 2018, 17:01

Vorne weg: Ich bin Python-Neuling und kein Informatiker, somit kann es auch einfach sein, dass ich nicht die richtigen Schlagworte für die Suche verwendet habe. Falls dem so ist, bin ich dankbar um jeden Link, sei es zu einer passenden Diskussion oder zu einem passenden Tutorial. Während des Schreibens ist mir aufgefallen, dass mir bereits die Formulierung konkreter Fragen schwer fällt, daher ist dieser Eintrag auch mehr eine Problembeschreibung als Fragestellung.

Ich muss mehrere Texte, die als txt vorliegen, in TEI-komforme XML-Dateien umwandeln. Das Schema liegt als RNG-Schema vor.
Wenn ich es richtig sehe, kann ich das, was ich suche, mittels XML.etree verwirklichen.

Dann zerfällt meine Aufgabe in folgende Teile:
1. Einlesen der Datei aus einem Verzeichnis (das Verzeichnis soll mittels Nutzerabfrage ermittelt werden)[machbar]
2. Einlesen des Schemas und Aufbau des XML-Baums [unklar]
3. Zeilenweiser Vergleich des Textes mit vorgegebenen regulären Ausdrücken [machbar, unklar bei Listen oder Absätzen innerhalb der <head>-Elemente von Unterpunkten und nummerierten Aufzählungen in Absätzen, vgl. Beispiel I.1. und II.1-3]
4. matchabhängige Zuweisung eines XML-Tags und Einordnung an der richtigen Stelle innerhalb der XML-Struktur [mittel xml.etree?, unklar]
5. Ausgabe in eine XML-Datei in ein Outputverzeichnis (ebenfalls per Nutzerabfrage) [machbar]

Zu 2.)
Ich könnte mittels xml.etree.ElementTree.SubElement die Struktur händisch aufbauen. Da das Schema allerdings komplexer ist, würde ich gerne den Dokumentenbaum/das Schema direkt einlesen, anstatt jedes Element einzeln erzeugen zu müssen. Geht das? Wenn ja, wie behandle ich Elemente, die auf verschiedenen Ebenen der XML-Struktur vorkommen können? vgl. unten <head>

Zu 3.)
Mein Problem ist hier vor allem im 2. und 3. Abschnitt der txt-Dateien, weil
a) Absätze in Untertiteln sich nicht durch Textmarken von einfachen Absätzen unterscheiden (beides beginnt jeweils mit Zeilenumbruch),
b) innerhalb von Absätzen, wenn Listen, wie die Unterüberschriften mit römischen Zahlen formatiert sind (im Gegensatz dazu kommen nur 4 verschiedene Titel vor und Untertitel beginnen immer mit einer römischen Ziffer).
zu a) Der Lösungsansatz wird hier vermutlich der Vergleich mit der Struktur des 1. Abschnitts liegen, in der nur die TOPs ohne Begleittext vorkommen, richtig? Vermutlich indem man den Textstream in die 3 Abschnitte splittet, die Struktur des ersten Teils für die beiden anderen übernimmt, Neues (im ersten Abschnitt nicht vorhandenes) entsprechend auszeichnet und danach wieder zusammensetzt.
zu b) Ich hatte überlegt hier die Behandlung anhand des parent-Elements zu steuern; sprich folgt eine Zeile mit dem Muster r"(\d\d?\.\s\V)" auf einen Absatz <p> ist es ein Listenelement <item>, ansonsten ist es der Untertitel <head> eines neue <div2>-Abschnitts. Allerdings folgen neue Untertitel bzw. <div2>-Abschnitte ja ebenfalls auf Absätze <p>. Also vermutlich eine ähnlich Lösung wie a)?

Auf stackoverflow habe ich eine Diskussion gefunden, die in etwa mein Problem behandelt, allerdings sind meine Dokument ungleich komplexer. Soweit ich es sehe. Ein Original kann ich leider nicht anhängen, da die Texte noch datenschutzrechtlichen Beschränkungen unterliegen, allerdings gibt es bereits veröffentlichte Beispiele hier, jedoch immer nur die Finalversion des Textes. In den txt-Dateien steht der Entwurf, eine kommentierte und die finale Version untereinander und soll auch entsprechend getaggt werden.

Die Struktur innerhalb einer txt-Datei entspricht:
Protokoll (Entwurf)
I. Überschrift
1. Unterüberschrift
2. Unterüberschrift
3. Unterüberschrift
II. Überschrift
1. Unterüberschrift
2. Unterüberschrift
Mit Umbruch
3. Unterüberschrift
a) mit
b) Listenpunkten
Protokoll (Einschätzung)
I. Überschrift
II. Überschrift
1. Unterüberschrift
Hier steht Text
2. Unterüberschrift
Mit Umbruch
Hier steht Text
3. Unterüberschrift
a) mit
b) Listenpunkten
a) Text zum Unterpunkt a
b) Text zum Unterpunkt b
Anhang
Anhang 1
Anhang 2
Hier steht Text
Protokoll (final)
I. Überschrift
1. Unterüberschrift
Hier steht Text
Mit einem zweiten Absatz
2. Unterüberschrift
Hier steht Text
3. Unterüberschrift
Hier steht Text
II. Überschrift
1. Unterüberschrift
Text mit Liste
- Eintrag
- Eintrag
- Eintrag
2. Unterüberschrift
Mit Umbruch
Hier steht Text
3. Unterüberschrift
a) mit
b) Listenpunkten
a) Text zum Unterpunkt a
b) Text zum Unterpunkt b
Anhang
Anhang 1 Teil1
Hier steht Text
Anhang 1 Teil2
1. Hier steht Text
2. in einer Liste
Wobei maximal 4 Überschriften und eine maximal 2-stellige Zahl von Unterüberschriften existieren.

Und daraus sollte folgendes XML entstehen (nicht valide und nur die wichtigsten Dokumentenäste, Elemente die weniger häufig vorkommen sind nicht aufgenommen):

Code: Alles auswählen

<?xml>
<TEI @xmlns @xmlns:xsi @version @xml:id>
<teiHeader/>
<text n=“Entwurf“>....</text>
<text n=“Einschätzung“>....</text>
<text n=“Final“>
    <front><head><title>Dokumententitel (final)</title><head></front>
    <body>
        <div1 n=“1“><head><title>I. Überschrift </title></head>
            <div2 n=“1“><head>1. Unterüberschrift </head>
                <p>Hier steht Text </p>
                <p>mit einem zweiten Absatz</p>
            <div2 n=“2“><head>2. Unterüberschrift</head></div2>
……
        </div1>
        <div1 n=“2“><head><title>II. Überschrift</title></head>
            <div2 n=“1“><head>1. Unterüberschrift </head>
                <p>Hier steht Text mit Liste
    <item n=“1“>Eintrag</item>
    <item n=“2“>Eintrag</item>
    <item n=“3“>Eintrag</item></list></p></div2>
            <div2 n=“2“><head>2. Unterüberschrift
                <lb/>mit Umbruch</head>
                <p/></div2>
            <div2 n=“3“><head>3. Unterüberschrift
            <list><item n=“1“>a) mit</item>
                <item n=“1“>Listenpunkten</item></list></head>
                <p><list><item n=“1“>a) Text zu a</item>
                    <item n=“2“>b) Text zu b</item></list></p></div2>
    </body>
    <back/>
    <group> <!—Anhang 1-->
        <text n=”1”> <!—Teil1-->
            <front/>
            <body/>
            <back/>
        <text/>
        <text n=”2”> <!—Teil2-->
        ….</text>
    </group>
</text></TEI>
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Montag 8. Januar 2018, 17:43

Eigentlich hab ich lxml schon ausgeschlossen gehabt, allerdings scheint es gegenüber ElementTree ja doch den Vorteil zu haben, dass es gleich Validierungsmöglichkeiten und RelaxNG-Unterstützung bietet, richtig?
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
__deets__
User
Beiträge: 2680
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 8. Januar 2018, 18:11

Kein triviales Problem. Zuerst mal würde ich so vorgehen, dass du die Verarbeitung des Dokumentes zu einer Datenstruktur trennst von der Ausgabe eben dieser Datenstruktur in XML.

Das klingt erst mal trivial, ist aber meines Erachtens ein wichtiger Schritt, um sich nicht im Wust von Verarbeitung und Ausgabe zu verlieren.

Prinzipiell ist dein Ansatz sinnvoll und die Analyse der Problemfälle wirkt auch richtig. Die Frage ist, ob es möglich ist die und Mehrdeutigkeiten, die du ja schon selber an identifiziert hast, durch Heuristiken aufzulösen. Wenn das nicht möglich ist, bleibt nur eine interaktive Lösung bei der du einen Menschen dazu heranziehst, die problematischen Entscheidungen für dich zu treffen.

Beides umzusetzen ist nicht einfach. Nehmen wir mal kurz als Beispiel deine römischen Aufzählungen. Wenn du das Dokument beginnst zu verarbeiten, weißt du, dass du zuerst mal mit einer Überschrift zu rechnen hast. Wenn dann eine Liste mit römischen Ziffern kommt, dann kannst du deren Anfang immer präzise erfassen. Denn sie muss (wohl Geformtheit vorausgesetzt) ja wieder mit einer I beginnen. Doch jetzt wird es schwierig. Alle folgenden Einträge Liste sind mehrdeutig . Beziehungsweise alle, für die du noch keine Überschrift identifiziert hast. Womit wir auf ein weiteres Problem kommen: die Interpretation eines konkreten Eintrags kann sich ändern abhängig davon, was man an anderen Stellen Dokument angenommen hat.

Wenn ich In deine Beispiele schau, dann fällt auf, dass nicht nur Struktur, sondern auch Formulierungen einem Schema folgen folgen. Gegebenenfalls kannst du auch das nutzen. Ich sehe zum Beispiel immer „ die Landesregierung“, „der Minister“ und so weiter.

Ein weiteres Problem sind Sätze, welche in einer Aufzählung zu einem Punkt gehören, aber freistehen. Das ist zumindest bei den letzten Sätzen welche den Aufzählungen folgen meines Erachtens technisch unter keinen Umständen lösbar. Gegebenenfalls gibt diese und andere Fälle den Ausschlag, das ganze semi automatisch zu lösen.

Last but not least: deine gesamten Überlegungen zum Thema XML Generierung und Schema sind meines Erachtens irrelevant. Grund dafür ist ganz einfach: ein Schema spielt nur eine Rolle beim einlesen von Dokumenten. Dein Generator muss Dokumente einem Schema entsprechen generieren, aber dazu bedarf es des Schemas bei der Generierung überhaupt nicht. Wenn du lästige Schreibarbeit von repetitiven Dokumentteilen vermeiden willst, kannst du immer noch ein eigenes XML ausspucken, welches die bekannte Struktur darstellen, und danach zum Beispiel mit XSLT eine Transformation ins gewünschte Ausgabeformat tätigen. Alternativ gehen natürlich auch Templatin Systeme in Python. Aber das ist alles Zukunftsmusik solange du dein einlesen Problem nicht gelöst hast.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Dienstag 9. Januar 2018, 12:41

__deets__ hat geschrieben:Kein triviales Problem.
Danke für die Antwort, jetzt komme ich mir nicht mehr ganz so blöd vor. Ein längerer Post meinerseits ist gestern leider vom Forum geschluckt worden und dann kam der Feierabend dazwischen, daher heute der zweite Versuch.

__deets__ hat geschrieben: Prinzipiell ist dein Ansatz sinnvoll und die Analyse der Problemfälle wirkt auch richtig. Die Frage ist, ob es möglich ist die und Mehrdeutigkeiten, die du ja schon selber an identifiziert hast, durch Heuristiken aufzulösen. Wenn das nicht möglich ist, bleibt nur eine interaktive Lösung bei der du einen Menschen dazu heranziehst, die problematischen Entscheidungen für dich zu treffen.
In diesem Fall würde ich gern eine semi-interaktive Lösung suchen, bei der wirklich nur unklares manuell korrigiert wird. Mir sitzt die Zeit im Nacken und ich müsste mich auch noch inhaltlich stärker mit den Texten auseinandersetzen.

__deets__ hat geschrieben:Wenn ich In deine Beispiele schau, dann fällt auf, dass nicht nur Struktur, sondern auch Formulierungen einem Schema folgen. Gegebenenfalls kannst du auch das nutzen. Ich sehe zum Beispiel immer „ die Landesregierung“, „der Minister“ und so weiter.
Ja, theoretisch schon. Eine "named entity recognition" soll in einem späteren Arbeitsschritt auch noch durchgeführt werden. Ich bin nur davon ausgegangen, dass es aufgrund von Mehrdeutigkeiten (Rau kann sowohl Adjektiv, als auch der Nachname des Ministerpräsidenten sein) wesentlich komplexer ist, wenn ich zuerst named entities erkennen will.

__deets__ hat geschrieben:Ein weiteres Problem sind Sätze, welche in einer Aufzählung zu einem Punkt gehören, aber freistehen. Das ist zumindest bei den letzten Sätzen welche den Aufzählungen folgen meines Erachtens technisch unter keinen Umständen lösbar. Gegebenenfalls gibt diese und andere Fälle den Ausschlag, das ganze semi automatisch zu lösen.
Das Problem habe ich bei den neuen Texten nicht, da alle bereits eine Bearbeitungsstufe durchlaufen haben. Bei der OCR entferne ich bereits alle unnötigen Absätze in den Texten, dazu gehören Zeilenumbrüche innerhalb von Aufzählungen und innerhalb von Aufzählungspunkten. Aber generell kann ich mich auch mit einer semi-automatischen Lösung anfreunden.

__deets__ hat geschrieben:Last but not least: deine gesamten Überlegungen zum Thema XML Generierung und Schema sind meines Erachtens irrelevant. Grund dafür ist ganz einfach: ein Schema spielt nur eine Rolle beim einlesen von Dokumenten.

Danke, du hast natürlich recht. Ich kann die Teile natürlich einfach benennen wie ich will und später transformieren. Hatte ich auch tatsächlich schon so überlegt, aber dann wieder verworfen, weil ich mir einen weiteren Arbeitsschritt sparen wollte. Manchmal braucht man jemanden, der einen mit dem Kopf auf eine Lösung stößt.

__deets__ hat geschrieben: Aber das ist alles Zukunftsmusik solange du dein einlesen Problem nicht gelöst hast.

In meine Kopf sehen die Arbeitsschritte zum Einlesen wie folgt aus:
1. Teile die txt anhand der Dokumentenköpfe in 3 Teile (Entwurf, Beurteilung, Final) PART.
2. Teile jedes dieser 3 Teil anhand der römischen Aufteilung in bis zu 4 Abschnitte SUBPART. Da die Titel der Abschnitte immer gleich sind - I.-IV. gefolgt von einer festen Zeichenfolgen - kann ich das über eine Mustererkennung lösen.
3. Sofern Zeilen mit Anhang beginnen, werden diese und die folgenden Zeilen, bis zum Beginn des nächsten PART mit BACK markiert
4. Ich vergleiche entsprechende Abschnitte des 1. Teils zeilenweise mit den Abschnitten aus den Teilen 2 und 3.
4.1 Gibt es keine Übereinstimmung, handelt es sich um einen Absatz P
4.2 Wenn es eine Übereinstimmung gibt, handelt es sich um eine Unterüberschrift TOP
5.1 Ist eine Zeile als TOP markiert und die vorhergehende Zeile oder die folgende Zeile ist als TOP markiert handelt es sich um einen Listenpunkt der Überschrift TOPITEM
5.2 Ist eine Zeile mit P markiert und beginnt mit einem der üblichen Listenzeichen, ist es ein Listeneintrag/eine Aufzählung LISTITEM
6. Ich wiederhole die Schritte 3-5.2 mit Abschnitten aus Teil 2 (Begründung) und 3 (Protokoll), die im Teil 1 nicht vorkommen.

PART, SUBPART, P, TOP, TOPITEM, LISTITEM, BACK können entweder nur Textmarken am Zeilenanfang sein oder schon XML-Tags nach dem Muster

Code: Alles auswählen

PART
    SUBPART
        TOP
            TOPITEM
        P
            LISTITEM
    BACK
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
__deets__
User
Beiträge: 2680
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 9. Januar 2018, 13:55

Kann ich jetzt nicht so wahnsinnig viel zu sagen. Weil zu einer Beurteilung eigentlich die Details fehlen. Ich kann mal kurz versuchen zu skizzieren, wie ich das machen würde:

Zuerst mal definiere ich ein Format, wie du es ja auch schon in deinem letzten Absatz erwähnst, dass mir den Text mit Annotationen versieht. Aus meiner Perspektive kann und sollte das durchaus XML sein, da du dann, ohne selbst ein Parser schreiben zu müssen, recht schnell zum Ziel kommst.

Der nächste Schritt Bestände für mich darin, alles bekannte Wissen über die formale Struktur, dessen ich habhaft werden kann, mit den Annotationen zu erfassen. Also zum Beispiel römische Aufzählung, andere Aufzählungen, einfacher Paragraph, und was sonst alles so möglich ist. Das Ergebnis ist im Grunde eine Liste, danke XML aber trotzdem eine Baumstruktur.

Auf diesen Baum kann man dann mit speziellen Transformatoren arbeiten. Das lehnt sich im Grunde an die Art und Weise, wie man in einer Programmiersprache mit abstrakten Sonntagsbäumen arbeitet, an. Das Ergebnis eines solchen Laufes ist das weitere anreichern mit Annotationen, oder gar eine Umstrukturierung. Ziel dabei ist natürlich Schlussendlich Eindeutigkeit zu erreichen. In dem Moment, wo ich Konflikte in den Annotationen habe, also mehrere Annotation, die sich widersprechen (zB Überrschrift und Aufzählung), kann ich einen händischen Lauf einlegen, in dem mir das entsprechende Konstrukt vorgelegt wird, mit der Auswahl der möglichen Annotationen. Durch die Auswahl erreiche ich dann hoffentlich für die nächsten Transformationsläufe Eindeutigkeit, so dass zum Beispiel Sektion verschiedene Art in die richtige Baumstruktur Überführt werden.

Von Vorteil ist es natürlich, diesen Vorgang schrittweise am besten unter Verwendung eines Versionskontrollsystems wie zum Beispiel GIT durchzuführen. Dadurch bekommt man Nachvollziehbarkeit, und kann gegebenenfalls wieder einen Schritt zurück gehen.

Eine weitere Möglichkeit besteht darin, jeden Text in einem ersten Lauf immer händisch qualifizieren zu lassen. Das Vorgehen ist im Grunde gleich, nur dass dir die automatisch erhobenen Annotationen genauso vorgelegt werden, es aber auch möglich ist, beliebig anders zur annotieren. Das ganze mit einer etwas optimierten Nutzerführung, wie man es zum Beispiel auch von der Kernel Konfiguration kennt. Also einfach Return zur Übernahme des defaults, und deinen Tasten 1-10 für die verschiedenen Möglichkeiten.
nezzcarth
User
Beiträge: 477
Registriert: Samstag 16. April 2011, 12:47

Dienstag 9. Januar 2018, 18:57

Marku5W hat geschrieben:Eine "named entity recognition" soll in einem späteren Arbeitsschritt auch noch durchgeführt werden. Ich bin nur davon ausgegangen, dass es aufgrund von Mehrdeutigkeiten (Rau kann sowohl Adjektiv, als auch der Nachname des Ministerpräsidenten sein) wesentlich komplexer ist, wenn ich zuerst named entities erkennen will.
Da du das indirekt schon ansprichst, vielleicht noch mal eine kurze Anmerkung: Wenn man eine generische Lösung für diese Fragestellung sucht, wäre die Verwendung von Methoden zur Verarbeitung natürlicher Sprache sicher sehr aussichtsreich. Named Entity Recognition ist dann ein Schritt in einer längeren NLP-Pipeline; in vorgelagerten Schritten werden zum Beispiel Wortarten annotiert (Pos-Tagging), was die Ambiguitäten, die du ansprichst (hoffentlich) weitestgehend auflöst. Allerdings würde ich Abstand davon nehmen, wenn die Zeit drängt und stattdessen eher die von deets vorgeschlagene Vorgehensweise verwenden. Da du ja glücklicherweise sehr strukturierte Texte mit vergleichweise geringer Variationsbreite hast, könnte das schneller zu guten Resultaten führen.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Mittwoch 10. Januar 2018, 13:22

@nezzcarth
Ja, hatte ich tatsächlich auch schon überlegt, aber aus den von dir genannten Gründen ausgeschlossen.
Die NER, die mir vorschwebt ist auch mehr eine "Mustererkennung", die bereits bekannte Entitäten entdecken soll. Nach dem Schema nehme String aus Liste, durchsuche Text, markiere Fundstellen, nehmen nächsten String.
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Mittwoch 10. Januar 2018, 13:45

Mittlerweile bin ich soweit, dass ich den Übungstext oben zerlegen kann, allerdings stoße ich immer auf das selbe Problem.
Ich zerlege den Text in zwei Schritten mit folgenden regex-Ausdrücken in eine verschachtelte Liste:

Code: Alles auswählen

textstufe = regex.compile('\\n(?=Protokoll)')
TO = regex.compile('((I+)V?)\\.(\\s([A-Z,Ü,a-z])+)\\n')
[/size]
Beim ersten Mal zerlegt es mir den Text schön und teilt jeweils vor dem Wort "Protokoll"

Code: Alles auswählen

'Protokoll (Entwurf)\nI. Überschrift\n1. Unterüberschrift\n(Hier steht Text1)\n2. Unterüberschrift\n(Hier steht Text2)\n3. Unterüberschrift\n(Hier steht Text3)\nII. Überschrift\n1. Unterüberschrift\n2. Unterüberschrift\nMit Umbruch\n3. Unterüberschrift\na) mit \nb) Listenpunkten',
'Protokoll (Einschätzung)\nI. Überschrift\nII. Überschrift\n1. Unterüberschrift\nHier steht Text2.1\n2. Unterüberschrift\nMit Umbruch\nHier steht Text2.3\n3. Unterüberschrift\na) mit \nb) Listenpunkten\na) Text zum Unterpunkt a\nb) Text zum Unterpunkt b\nATO-Liste\nAnhang\nAnhang 1\nAnhang 2\nHier steht Text a',
'Protokoll (final)\nI. Überschrift\n1. Unterüberschrift\nHier steht Text1.1.1\nMit einem zweiten Absatz\n2. Unterüberschrift\nHier steht Text1.1.2\n3. Unterüberschrift \nHier steht Text1.1.3\nII. Überschrift\n1. Unterüberschrift\nText mitListe\n- Eintrag\n- Eintrag\n- Eintrag\n2. Unterüberschrift\nMit Umbruch\nHier steht Text1.2.1\n3. Unterüberschrift\na) mit \nb) Listenpunkten\na) Text zum Unterpunkt a\nb) Text zum Unterpunkt b\nIII. ATO \n1. Unterüberschrift\nHier steht Text3.1\n2. Unterüberschrift\nHier steht Text3.2\n3. Unterüberschrift\nHier steht Text3.3\nAnhang\nAnhang 1 Teil1\nHier steht Text a1.1\nAnhang 1 Teil2\n1. Hier steht Text a2.1\n2. in einer Liste']
[/size]
Beim zweiten Mal, unterteile ich jedes Listenfeld erneut in eine Liste

Code: Alles auswählen

for n in range(len(liste)):
     liste[n] = regex.split(TO, liste[n])
     liste[n]
     n += 1
[/size]Allerdings trennt er nun nicht mehr sauber vor dem ersten großen I, sondern spuckt mir folgendes aus :

Code: Alles auswählen

['Protokoll (Einschätzung)\n',
 'I. Überschrift',
 'I',
 'I',
 'Überschrift',
 '\n',
 'II. Überschrift',
 'II',
 'II',
 'Überschrift',
 '\n1. Unterüberschrift\nHier steht Text2.1\n2. Unterüberschrift\nMit Umbruch\nH
ier steht Text2.3\n3. Unterüberschrift\na) mit \nb) Listenpunkten\na) Text zum U
nterpunkt a\nb) Text zum Unterpunkt b\nATO-Liste\nAnhang\nAnhang 1\nAnhang 2\nHi
er steht Text a']
[/size]

Wie erreiche ich es, dass ich eine Unterteilung nur vor den römischen Ziffern bekomme "\b(?=...)" scheint er mit regex.split zu ignorieren.
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
__deets__
User
Beiträge: 2680
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 10. Januar 2018, 14:50

Ich würde das zeilenweise machen. Statt zu probieren gleich ganze Klumpen zu erzeugen die du subdividierst. Das rächt sich wenn du Änderungen in der Struktur vornehmen willst.

Und dein n+=1 ist wirkungslos. Es ist auch üblicher in Python eine neue Liste zu machen statt die alte hinzubiegen. Das wird dann auch vom Code eleganter.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Dienstag 23. Januar 2018, 18:08

So nachdem ich jetzt eine Weile gebastelt hab, wollte ich mich hier nochmal kurz bedanken. In erster Linie bei __deets__, der mich in die richtige Richtung gestoßen hat. Das Problem mit dem NER werde ich separat lösen (und das ist auch gut so) und wahrscheinlich liese sich mein Code noch an etwa 1000 Stellen verbessern, aber hinten kommt jetzt eine XML-Datei raus, die ich dann mit XSLT weiter verarbeiten kann.
Dafür, dass ich vor etwa 2,5 Wochen hier mit einer gerade mal groben Ahnung von dem, was ich machen wollte, aufgeschlagen bin, bin ich zufrieden und hab jetzt noch mehr Respekt vor den Leuten, die Parser schreiben, die sehr viel universeller sind als meiner.
Das ganze Ding hat mein Interesse tiefer in die Verarbeitung von Texten einzusteigen geweckt, allerdings werde ich mich jetzt wohl erstmal anderen Dingen widmen müssen.
Falls euch das Monster, dass ich geschaffen habe, interessiert:

Code: Alles auswählen

class KS2mku():
		
	def __init__(self):
		import sys
		
		__fname, __pfad = self.abfrage()
		
		try:
			__pfad != None
			sys.path.append(__pfad)
			self._pfad = __pfad
		except: 
			print('das hat nicht geklappt')
			print(sys.exc_info()[0])
			raise
		
		if self._pfad.endswith('\\'):
			self.__eingabe = __pfad+__fname+'.txt'
			self._ausgabe = __pfad+__fname+'_ausgabe.txt'
		elif self._pfad.endswith('/'):
			self.__eingabe = __pfad+__fname+'.txt'
			self._ausgabe = __pfad+__fname+'_ausgabe.xml'
		else: 
			self.__eingabe = __pfad+'/'+__fname+'.txt'
			self._ausgabe = __pfad+'/'+__fname+'_ausgabe.txt'
					
		self._ergebnis = self.auszeichnung(self.__eingabe)
		
		try:
			self.ausgabe(self._ergebnis, self._ausgabe)
			print('Erfolgreich')
		except IOError as e:
			errno, strerror = e.args
			print ("I/O error({0}): {1}".format(errno,strerror))
		except:
			print("Unexpected error:", sys.exc_info()[0])
			raise	
			
	@classmethod
	def abfrage(cls):
		import os, sys
		
		try:
			cls._pfad
			if cls._pfad != None:
				c = cls._pfad
				print('Der vorhandenen Pfad '+cls._pfad+' wurde verwendet.')
			else: 
				c = input('Bitte den Ordnerpfad angeben: ')
				if c == None:
					c = os.getcwd()
		except NameError:
			print('Es war keine Pfad vorhanden.')
			c = input('Bitte den Ordnerpfad angeben: ')
			if c == None:
				c = os.getcwd()
		except AttributeError:
			print('Es war keine Pfad vorhanden.')
			c = input('Bitte den Ordnerpfad angeben: ')
			if c == None:
				c = os.getcwd()
				
		except:
			print("Unexpected error:", sys.exc_info()[0])
			raise
			
		n = input('Bitte den Dateinamen ohne Endung angeben: ')
		
		return n, c
		
	@staticmethod
	def ausgabe(ergebnis, fout):
		import xml.etree.ElementTree as ET
		
		#with open(fout, mode='w+', encoding='UTF-8') as a:
		ergebnis.write(fout, encoding='utf-8', xml_declaration=True)
			
	@staticmethod
	def rexe(expr):
			import regex
			expr = str(expr)
			expr = expr.replace(')', '\)')
			expr = expr.replace('(', '\(')
			rex = regex.compile(expr, flags=regex.V1)
			return rex
			
	@staticmethod
	def userprompt(stat, expr):
			import sys
			print('\n\n\n\n-----------------------------')
			print('Dies ist die aktuelle Zeile:')
			print('\n'+expr+'\n')
			InfoStat = {'HEAD': 1,
						'HEADPart': 1,
						'HEADPartTn': 1,
						'TOG': 2,
						'TOP': 3,
						'TOPPart': 3,
						'Absatz': 4,
						'Liste': 5,
						'BACK': 6,
						'Umbruch': stat,
						'Kommentar': stat,
						'ignorieren': stat
						}			
			try:
				c = input('Bitte geben sie die richtige Auszeichnungsstufe ein:\nHEAD\nHEADPart\nHEADPartTn\nTOG\nTOP\nTOPPart\nAbsatz\nListe\nBACK\nUmbruch\nKommentar\nignorieren:\n\n')
			except KeyError:
				c = input('Fehler in der Eingabe.\nBitte erneut eingeben.')
			return (InfoStat[c], c)
			
	
	@classmethod
	def auszeichnung(cls, eingabe):
		
		'''	# Implementiert einen finiten Automaten, der die Zustände der		
			# Auszeichnungsstufe enthält										
			# You can visualize it using pencil and paper -- draw small circles 
			# with the status numbers inside (called nodes in the graph theory).
			# Being at the status, you allow only some kind of input (line). 	
			# When the input is recognized, you draw the arrow (oriented edge 	
			# in the graph theory) to another status (possibly to the same		
			# status, as a loop printing back to the same node). The arrow is 	
			# annotated `condition | action'.									
			# Hier werden die Knoten des XML-Baums als Zustände genommen. '''
		
		import regex, sys
		import xml.etree.ElementTree as ET
		
		print('Eingelesen')
		
		with open(eingabe) as f:
		
			HEADrex = regex.compile('(m?)Tagesordnung|(24 Ausfertigungen)', flags=regex.V1)
			HEADPartTnrex = regex.compile('(?m)^(((Finanzm|Innenm|Justizm|Kulutsm|M)inister(präsident)?)|Staatssekretär|Sprecher der Landesregierung|zugleich)')
			TOGrex = regex.compile('(?m)(^I{1,3}V?\\.\\s.*$)|(ATO\s?-\s?Liste)', flags=regex.V1)
			backrex = regex.compile('(?m)^(Entwurf|Anlage|I\\s?A\\s?\\d)', flags=regex.V1)
			TOPrex = regex.compile('(?m)^(\\d\\d?\\.\\s.*$)|Antrag|Die nächste Kabinettssitzung)', flags=regex.V1)
			TOPPartrex = regex.compile('(?m)^(hier|Vorschlag|Mitteilung)', flags=regex.V1)
			seiterex = regex.compile('(?m)-?\\s?\\d\\d?\\s?-\\s*?\\d?\\n|^\\d\\d?\\..\\n', flags=regex.V1)
			listerex = regex.compile('(?m)^-\\s?\)|^[a-z][a-z]?\\s?\)|^gemäß|^Drucksache|^zu Drucksache|^\\d\\d?\\.?\\)|^-\\s[A-z]{3}', flags=regex.V1)
			sicrex = regex.compile('\\[sic!\\]', flags=regex.V1)
			verantwortlichrex = regex.compile('(?m)^-\\s?[A-Z]([a-z]|[A-Z])?\\s?-?', flags=regex.V1)
			ausschlussrex = regex.compile('''(?m)(^(
												Beschlossen|
												Das Kabinett beschlie(ß|ss)t|
												Unproblematisch|
												Die Landesregierung (stimmt dem Gesetz zu|tritt dafür ein|beschlie(ß|ss)t)|
												\\((Gemeinsame\s)?Kabinettvorlage.*$|
												\\(Personalvorschlag
												\\(Angelegenheit\s.*$))''', flags=regex.V1)
			
			root = ET.Element('root')
			root.text = '\n'
			
			status = 0
			n = 0
			
			for line in f:
				n += 1
				try:
					rex = cls.rexe(line)
					test = len(regex.findall(rex, open(eingabe).read()))
					
					if sicrex.match(line):
						status, key = cls.userprompt(status, line)
						text = {key:line}
					elif line.startswith('<'):
						status, key = cls.userprompt(status, line)
						text = {key:line}
					# elif seiterex.match(line):
						# umbruch = ET.Element('umbruch')
						# umbruch.attrib = {'n' : line}
						# for keys in text:
							# if keys == 'HEAD':
								# HEAD.append(umbruch)
							# elif keys == 'HEADPartTn':
								# HEADPartTn.append(umbruch)
							# elif keys == 'HEADPart':
								# HEADPart.append(umbruch)
							# elif keys == 'TOG':
								# TOG.append(umbruch)
							# elif keys == 'TOP':
								# TOP.append(umbruch)
							# elif keys == 'TOPPart':
								# TOPPart.append(umbruch)
							# elif keys == 'Absatz':
								# Absatz.append(umbruch)
							# elif keys == 'Liste':
								# liste.append(umbruch)
							# elif keys == 'ListeEintrag':
								# eintrag.append(umbruch)
							# elif keys == 'BACK':
								# TOG.append(umbruch)
					# elif regex.match(regex.compile('(?m)^/n'), line):
						# text = {'igorieren':'Leerzeile'}
						
					if status == 0:							# Beginn eines Textes
						if HEADrex.match(line):
							text = {'HEAD':line}
							status = 1
						else:
							status = cls.userprompt(status, line)
							text = {status:line}
					
					elif status == 1:						# Kopf des Dokuments
						if TOGrex.match(line):
							if test > 1:
								text = {'TOG':line}
								status = 2
							else: 
								status, key = cls.userprompt(status, line)
								text = {key:line}
						elif line.startswith('Es wird festgestellt'):
							text = {'HEADPart':line}
						elif HEADPartTnrex.match(line):
							text = {'HEADPartTn':line}
						else: 
							text = {'HEADPart':line}
					
					elif status == 2:						# Gliederung 1. Ordnung, KP: Bundesangelegenheiten, Landesangelegenheiten etc.
						if test > 1:
							if TOGrex.match(line):
								text = {'TOG':line}
							elif TOPrex.match(line):
								text = {'TOP':line}
								status = 3
							elif backrex.match(line):
								text = {'BACK':line}
								status = 6
							elif ausschlussrex.match(line):
								text = {'Absatz':line}
								status = 4
							elif verantwortlichrex.match(line):
								text = {'Absatz':line}
								status = 4
							else: 
								status, key = cls.userprompt(status, line)
								text = {key:line}
						elif backrex.match(line):
							text = {'BACK':line}
							status = 6
						else:
							status, key = cls.userprompt(status, line)
							text = {key:line}
								
					elif status == 3:						# Gliederung 2. Ordnung, KP: TOP
						if test > 1:
							if TOGrex.match(line):
								text = {'TOG':line}
								status = 2
							elif TOPrex.match(line):
								text = {'TOP':line}
								status = 3
							elif 'kommission der europäischen gemeinschaft' in line.lower() or 'kommission der europäischen gemeinschaften' in line.lower():
								text = {'TOP':line}
							elif ausschlussrex.match(line):
								text = {'Absatz':line}
								status = 4
							elif listerex.match(line):
								text = {'TOPPart':line}
								status = 5
							elif verantwortlichrex.match(line):
								text = {'Absatz':line}
								status = 4
							elif TOPPartrex.match(line):
								text = {'TOPPart':line}
							elif backrex.match(line):
								text = {'BACK':line}
								status = 6
							elif HEADrex.match(line):
								text = {'HEAD':line}
								status = 1
							else: text = {'TOPPart':line}
						elif 'kommission der europäischen gemeinschaft' in line.lower() or 'kommission der europäischen gemeinschaften' in line.lower():
							text = {'TOP':line}
						elif verantwortlichrex.match(line):
							text = {'Absatz':line}
							status = 4
						elif TOPPartrex.match(line):
							text = {'TOPPart':line}
						elif TOGrex.match(line):
							status, key = cls.userprompt(status, line)
							text = {key:line}
						elif HEADrex.match(line):
							text = {'HEAD':line}
							status = 1
						else: 
							text = {'Absatz':line}
							status = 4
							
					elif status == 4:						# Absätze innerhalb eines TOPs
						if HEADrex.match(line):
							text = {'HEAD':line}
							status = 1
						elif TOGrex.match(line):
							if test > 1:
								text = {'TOG':line}
								status = 2
							elif 'ATO-Liste' in line:
								text = {'TOG':line}
								status = 2
							else: 
								status, key = cls.userprompt(status, line)
								text = {key:line}
						elif ausschlussrex.match(line):
							text = {'Absatz':line}
							status = 4
						elif listerex.match(line):
							if test > 1: 
								if TOPrex.match(line):
									text = {'TOP':line}
									status = 3
								elif verantwortlichrex.match(line):
									text = {'Absatz':line}
									status = 4
								elif ausschlussrex.match(line):
									text = {'Absatz':line}
								elif TOPPartrex.match(line):
									status, key = cls.userprompt(status, line)
									text = {key:line}
								else: 
									text = {'Liste':line}
									status = 5
							else: 
								text = {'Liste':line}
								status = 5
						elif seiterex.match(line):
							text = {'Umbruch':line}
						elif verantwortlichrex.match(line):
							text = {'Absatz':line}
						elif TOPrex.match(line):
							text = {'TOP':line}
							status = 3
						elif backrex.match(line):
							text = {'BACK':line}
							status = 6
						else: text = {'Absatz':line}

					elif status == 5:						# Listen und Aufzählungen innerhalb eines Absatzes
						if HEADrex.match(line):
							text = {'HEAD':line}
							status = 1
						elif test > 1:
							if TOGrex.match(line):
								text = {'TOG':line}
								status = 2
							elif TOPrex.match(line):
								text = {'TOP':line}
								status = 3
							elif backrex.match(line):
								text = {'BACK':line}
								status = 6
							else:
								status, key = cls.userprompt(status, line)
								text = {key:line}
						elif listerex.match(line):
							text = {'Liste':line}	
						elif backrex.match(line):
							text = {'BACK':line}
							status = 6
						elif ausschlussrex.match(line):
							text = {'Absatz':line}
							status = 4
						elif TOPrex.match(line):
							text = {'TOP':line}
							status = 3
						else: 
							status, key = cls.userprompt(status, line)
							text = {key:line}
					
					elif status == 6:				# Start des Anhangs/RÜckenteils
						if backrex.match(line):
							text = {'BACK':line}
						elif HEADrex.match(line):
							text = {'HEAD':line}
							status = 1	
						elif TOGrex.match(line):
							text = {'TOG':line}
							status = 2
						elif TOPrex.match(line):
							text = {'TOP':line}
							status = 3
						elif backrex.match(line):
							text = {'BACK':line}
							status = 6
						elif ausschlussrex.match(line):
							text = {'Absatz':line}
							status = 4
						elif listerex.match(line):
							text = {'Aufzählung':line}
							status = 5
						else:
							text = {'Absatz':line}
					
					else: 		
						status, key = cls.userprompt(status, line)
						text = {key:line}

					for keys in text:
						if keys == 'HEAD':
							Abschnitt = ET.SubElement(root, 'text')
							Abschnitt.text = '\n'
							Abschnitt.attrib = {'n':line}
							HEAD = ET.SubElement(Abschnitt, 'HEAD')
							HEAD.text = line
							HEAD.tail ='\n'
						elif keys == 'HEADPartTn':
							HEADPartTn = ET.SubElement(HEAD, 'HEADPartTn')
							HEADPartTn.text = line
							HEADPartTn.tail = '\n'
						elif keys == 'HEADPart':
							HEADPart = ET.SubElement(HEAD, 'HEADPart')
							HEADPart.text = line
							HEADPart.tail = '\n'
						elif keys == 'TOG':
							TOG = ET.SubElement(Abschnitt, 'TOG')
							TOG.attrib = {'n':line}
							TOG.text = line+'\n'
							TOG.tail = '\n'
						elif keys == 'TOP':
							TOP = ET.SubElement(TOG, 'TOP')
							TOP.text = text[keys]+'\n'
							TOP.tail = '\n'
						elif keys == 'TOPPart':
							TOPPart = ET.SubElement(TOP, 'TOPPart')
							TOPPart.text = line
							TOPPart.tail = '\n'
						elif keys == 'Absatz':
							Absatz = ET.SubElement(TOP, 'Absatz')
							Absatz.text = line
							Absatz.tail = '\n'
						elif keys == 'Liste':
							liste = ET.SubElement(TOP, 'liste')
							liste.text = line
							liste.tail = '\n'
						elif keys == 'ListeEintrag':
							eintrag = ET.SubElement(liste, 'eintrag')
							eintrag.text = line
							eintrag.tail = '\n'
						elif keys == 'BACK':
							TOG = ET.SubElement(Abschnitt, 'TOG')
							TOG.attrib = {'name':'BACK'}
							TOG.text = line
							TOG.tail = '\n'
						elif keys == 'Umbruch':
							umbruch = ET.Element('umbruch')
							umbruch.attrib = {'n' : line}
							try:
								Absatz.append(umbruch)
							except:
								print("Unexpected error:", sys.exc_info()[0])
								ET.dump(root)
								continue
						else: pass #keys == 'ignorieren':
							# pass
				except:
					ET.dump(root)
					print('\nStatus: '+str(status)+'\n'+str(n)+': '+line+str(text)+'\n')
					print("Unexpected error:", sys.exc_info()[0])
					raise
					
			tree = ET.ElementTree(root)
			return tree
			print('Abgeschlossen')
			
			
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
Sirius3
User
Beiträge: 7594
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 23. Januar 2018, 19:11

@Marku5W: in Python muß man nicht alles in Klassen stopfen, wie es z.B. in Java nötig wäre, mit anderen Worten, die Klasse ist nur unnötiger Overhead, der ersatzlos gestrichen werden kann. Importe gehören an den Anfang der Datei. Eingerückt wird immer mit 4 Leerzeichen pro Ebene.

Zeile 6: die Doppelten Unterstriche haben hier nichts zu suchen
Zeile 9: Vergleiche, die nichts machen können weg
Zeile 12: das Except hier macht keinen Sinn, weil es nichts gibt, was sinnvollerweise im try schief gehen könnte.
Zeile 17-25: das könnte man einfach per os.path.join lösen
Zeile 32ff: sinnfreies Exceptionhandling
Zeile 50: input liefert nie None zurück
Zeile 52: ein NameError ist ein Programmierfehler, sollte also nicht auftreten
Zeile 57: den AttributeError sollte hier auch nicht auftreten
Zeile 69: n und c sind keine aussagekräftigen Namen
Zeile 82/83: das replace macht genau nichts
Zeile 89: schon wieder ein Import, der nicht gebraucht wird
Zeile 108: ein KeyError kann hier nicht auftreten
Zeile 159: wenn Du einen Index brauchst, nimm enumerate
Zeile 163: kannst Du erklären, was diese Zeile machen soll???? Für jede Zeile der Eingabe liest Du nochmal die komplette Eingabe und suchst die Zeilen, die dem regulären Ausdruck einer anderen Zeile der selben Datei entspricht? Das macht überhaupt keinen Sinn.
Zeile 206-398: das ist viel zu lang. Teile das in sinnvolle Funktionen auf
Zeile 400: text hat immer genau einen Schlüssel, die for-Schleife ist also überflüssig. keys ist der falsche Name für EINEN Schlüssel.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Donnerstag 1. Februar 2018, 11:05

@sirius3 Danke für die Korrekturen, ich bin die letzten Tage leider wieder mit anderem beschäftigt worden, so dass ich noch ein bisschen brauche, bis ich wieder dazu komme das Skript weiter zu bearbeiten. Aber du hast natürlich mit allem Recht, jetzt wo ich es lese, wird es mir auch klar.

Die Aufteilung in Klassen repräsentiert übrigens nur meine Gedankengänge bzw. die Teilaufgaben, die ich nacheinander angegangen bin. Ich bin Geisteswissenschaftler und bis zu diesem Projekt jetzt, beschränken sich meine Programmiererfahrungen auf etwas Basic so zu Grundschulzeiten und ein bisschen Delphi/Pascal als ich vor langer, langer Weile für 3 Semester dachte, ich müsste Mechatroniker werden.
Vielleicht sollte ich mir mal eine Schulung zu Java genehmigen lassen...
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Donnerstag 22. März 2018, 18:04

Sirius3 hat geschrieben:@Marku5W: in Python muß man nicht alles in Klassen stopfen, wie es z.B. in Java nötig wäre, mit anderen Worten, die Klasse ist nur unnötiger Overhead, der ersatzlos gestrichen werden kann. Importe gehören an den Anfang der Datei. Eingerückt wird immer mit 4 Leerzeichen pro Ebene.

Zeile 6: die Doppelten Unterstriche haben hier nichts zu suchen
Zeile 9: Vergleiche, die nichts machen können weg
Zeile 12: das Except hier macht keinen Sinn, weil es nichts gibt, was sinnvollerweise im try schief gehen könnte.
Zeile 17-25: das könnte man einfach per os.path.join lösen
Zeile 32ff: sinnfreies Exceptionhandling
Zeile 50: input liefert nie None zurück
Zeile 52: ein NameError ist ein Programmierfehler, sollte also nicht auftreten
Zeile 57: den AttributeError sollte hier auch nicht auftreten
Zeile 69: n und c sind keine aussagekräftigen Namen
Zeile 82/83: das replace macht genau nichts
Zeile 89: schon wieder ein Import, der nicht gebraucht wird
Zeile 108: ein KeyError kann hier nicht auftreten
Zeile 159: wenn Du einen Index brauchst, nimm enumerate
Zeile 163: kannst Du erklären, was diese Zeile machen soll???? Für jede Zeile der Eingabe liest Du nochmal die komplette Eingabe und suchst die Zeilen, die dem regulären Ausdruck einer anderen Zeile der selben Datei entspricht? Das macht überhaupt keinen Sinn.
Zeile 206-398: das ist viel zu lang. Teile das in sinnvolle Funktionen auf
Zeile 400: text hat immer genau einen Schlüssel, die for-Schleife ist also überflüssig. keys ist der falsche Name für EINEN Schlüssel.
So nach längere Pause konnte ich wieder weiter machen und hab mich zunächst mal der Monitaliste von @Sirius3 gewidmet. Ich werde den korrigierten Code erstmal nicht einstellen, sondern die Liste mit meinen Anmerkungen versehen.
  • Zeile 6: die Doppelten Unterstriche haben hier nichts zu suchen
    Zeile 9: Vergleiche, die nichts machen können weg
    Zeile 12: das Except hier macht keinen Sinn, weil es nichts gibt, was sinnvollerweise im try schief gehen könnte.
    Zeile 32ff: sinnfreies Exceptionhandling
    Habe ich entfernt.
  • Zeile 17-25: das könnte man einfach per os.path.join lösen
    Geändert
  • Zeile 50: input liefert nie None zurück
    Die Idee war, dass der Benutzer statt eines Pfades einfach einen leeren String zurück gibt, wenn er das Skript aus dem Arbeitsordner heraus startet. Die Zeile müsste dann richtiger Weise so aussehen:

    Code: Alles auswählen

     if c == "":
    . Oder?
  • Zeile 52: ein NameError ist ein Programmierfehler, sollte also nicht auftreten
    Ist für mich auch nur zum Debuggen gedacht. Da ich noch dabei bin, das Skript zu verfeinern, traue ich mir durchaus zu einen Fehler zu machen, der dazu führt, dass die Variable _pfad nicht existiert.
  • Zeile 57: den AttributeError sollte hier auch nicht auftreten
    Der ist entfernt
  • Zeile 69: n und c sind keine aussagekräftigen Namen
    Ist das auf den 20 Zeilen, wo diese eine Rolle spielen tatsächlich wichtig? Versteh mich nicht falsch, aber um zu verstehen, was in dem Abschnitt passiert, muss ich mir doch sowieso die ganze Methode anschauen?
  • Zeile 82/83: das replace macht genau nichts
    An der Stelle werden Regex-Ausdrücke zum Abgleich mit dem Dokument generiert. Wenn ich diese Ersetzung in der eben eingelesenen Zeile nicht durchführe, bekomme ich Fehler, wenn in der Zeile einzelne schließende Klammern sind - wie z.B. bei Aufzählungen a), b) oder 1.), 2.) etc. Ich lass mich aber gern belehren, wie es besser geht.
  • Zeile 89: schon wieder ein Import, der nicht gebraucht wird
    Der war tatsächlich überflüssig und ein Artefakt einer anderen Version.
  • Zeile 108: ein KeyError kann hier nicht auftreten
    Der KeyError tritt dann auf, wenn ich schusselig bin und ab und an vergesse, dass Dictionary aus Z. 93 zu aktualisieren oder wenn sich jemand bei der Eingabe vertippt. Mit ist nicht klar, wie ich diese Ausnahme sonst abfangen soll?
  • Zeile 159: wenn Du einen Index brauchst, nimm enumerate
    Jetzt kenne ich auch enumerate.
  • Zeile 163: kannst Du erklären, was diese Zeile machen soll???? Für jede Zeile der Eingabe liest Du nochmal die komplette Eingabe und suchst die Zeilen, die dem regulären Ausdruck einer anderen Zeile der selben Datei entspricht? Das macht überhaupt keinen Sinn.
    Meine Texte sind unterschiedlich lang, inhaltlich nicht gleich, und die Strukturen einer Zeile nicht eindeutig.
    Eine arabische Zahl am Anfang leitet die Überschrift eines Tagesordnungspunktes ein, den ersten Punkt einer nummerierten Liste, ein Datum etc.; römische Ziffern sind der Beginn eines Gliederungsabschnitts, nummerierter Listen, von Namen, Dokumenten etc.
    a), b), c) sind entweder Listen innerhalb von Absätzen oder Untertitel von Tagesordnungspunkten, daneben existieren Untertitel die durch Signalwörter eingeleitet werden und Untertitel, die nur aufgrund des Layouts im Originaldokument als solche gekennzeichnet sind.
    Die einzige Entscheidungsgrundlage um Überschriften und Unterüberschriften von normalen Absätzen und Listen zu trennen ist, dass jede Überschrift mindestens zweimal in einem Text vorhanden ist.
    Sprich entspricht die Zeile einer weiteren Zeile handelt es sich um eine Überschrift oder Unterüberschrift, abhängig von der davor stehenden Zeile und der internen Struktur.
  • Zeile 206-398: das ist viel zu lang. Teile das in sinnvolle Funktionen auf
    Hier bin ich etwas überfordert, denn mir fehlen die Ideen. Zu dem eben Gesagten kommt hinzu, dass auch einige Absätze doppelt bzw. mehrfach vorkommen. Ich treffe daher in jedem Status die Entscheidung einfach <-> mehrfach und wenn mehrfach entspricht die Zeile einem der Muster, die auf dieser Ebene vorkommen können in der Reihenfolge ihres wahrscheinlichen Auftretens.
  • Zeile 400: text hat immer genau einen Schlüssel, die for-Schleife ist also überflüssig. keys ist der falsche Name für EINEN Schlüssel.
    Jupp, ist geändert.
Mir ist noch eingefallen, wie ich euch ein besseres Verständnis für meine Texte bieten kann:
Unter http://protokolle.archive.nrw.de/ und unter http://www.archive.nrw.de/LAV_NRW/jsp/e ... viId=6&y=0 stehen unter den Punkte Protokolle bzw. Kabinettprotokolle jeweils redigierte Texte (d.h. unter Berücksichtigung von Datenschutz und Persönlichkeitsrechten überarbeitet Texte) der finalen Textstufe "Protokoll" zur Verfügung. Ich sitze an den Texten der 9. Legislaturperiode und jetzt sollen erstmals alle 3 Textstufen "Entwurf", "Beurteilung" und "Protokoll" ediert werden. Vielleicht wird so einiges klarer?
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
Sirius3
User
Beiträge: 7594
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 22. März 2018, 21:12

Marku5W hat geschrieben: Ist für mich auch nur zum Debuggen gedacht. Da ich noch dabei bin, das Skript zu verfeinern, traue ich mir durchaus zu einen Fehler zu machen, der dazu führt, dass die Variable _pfad nicht existiert.
Das ist kein Grund, etliche Exceptions zu schreiben; das Argument würde ja für jede Zeile Code gelten. Daher, weglassen und Programmierfehler korrigieren, wenn das Programm abbricht. Und ja, Namen sind wichtig, denn beim Lesen versteht man dann sofort, für was die Variable da ist, ohne rätseln zu müssen.
Die Funktion sähe also so aus:

Code: Alles auswählen

def abfrage(): # besser pfad_abfragen
    path = input('Bitte den Ordnerpfad angeben: ')
    path = os.path.abspath(path or '.')
    filename = input('Bitte den Dateinamen ohne Endung angeben: ')
    return filename, path
Marku5W hat geschrieben: An der Stelle werden Regex-Ausdrücke zum Abgleich mit dem Dokument generiert. Wenn ich diese Ersetzung in der eben eingelesenen Zeile nicht durchführe
Ja, da hatte ich mich verguckt. Aber der Sinn ist mir doch nicht ganz klar, warum bei 1.) die Klammer keine Bedeutung haben soll, der Punkt aber schon? Wenn alle Sonderzeichen keine Bedeutung haben sollen, dann stellt sich mir die Frage, warum überhaupt reguläre Ausdrücke?
Marku5W hat geschrieben:Der KeyError tritt dann auf, wenn ich schusselig bin
Ja aber ein KeyError kann da überhaupt nicht auftreten! Egal ob schusselig oder nicht.
Am besten generiert man die Schlüssel automatisch und macht eine Schleife bei Fehleingaben.

Code: Alles auswählen

def userprompt(stat, expr):
    print('\n\n\n\n-----------------------------')
    print('Dies ist die aktuelle Zeile:')
    print('\n'+expr+'\n')
    info_stat = [
        ('HEAD', 1),
        ('HEADPart', 1),
        ('HEADPartTn', 1),
        ('TOG', 2),
        ('TOP', 3),
        ('TOPPart', 3),
        ('Absatz', 4),
        ('Liste', 5),
        ('BACK', 6),
        ('Umbruch', stat),
        ('Kommentar', stat),
        ('ignorieren', stat),
    ]
    while True:
        key = input('Bitte geben sie die richtige Auszeichnungsstufe ein:\n'
            '{}:\n\n'.format('\n'.join(k for k,v in info_stat))
        )
        try:
            return dict(info_stat)[key], key
        except KeyError:
            print('Fehler in der Eingabe.\nBitte erneut eingeben.')
Zu Zeile 163: hier bestätigt sich also, dass Du gar keine regulären Ausdrücke brauchst. Und wenn Du 10000 Zeilen hast, liest Du 10000mal die gesamte Datei. Du merkst hoffentlich selbst, dass das ziemlich umständlich ist. Gleiche Zeilen zählt man einmal am Anfang z.B. mit `Counter`.
Marku5W
User
Beiträge: 10
Registriert: Montag 8. Januar 2018, 14:21
Kontaktdaten:

Freitag 23. März 2018, 13:17

Sirius3 hat geschrieben: Zu Zeile 163: hier bestätigt sich also, dass Du gar keine regulären Ausdrücke brauchst. Und wenn Du 10000 Zeilen hast, liest Du 10000mal die gesamte Datei. Du merkst hoffentlich selbst, dass das ziemlich umständlich ist. Gleiche Zeilen zählt man einmal am Anfang z.B. mit `Counter`.
Und wieder was gelernt, danke. So geht es tatsächlich eleganter, wenn man 'Counter' erstmal kennt. Ich denke da anscheinend noch zu analog, händisch würde man schließlich auch Zeile für Zeile vergleichen und nicht erst eine Liste mit der Häufigkeit von Zeilen anlegen.

Vielleicht kannst du mir auch bei einem anderen Problem helfen. Einige Zeilen enthalten bereits strukturelles Markup, das für den Editionsprozess wichtig ist und der Einfachheit halber bereits vorher eingetragen wird, Beispiele sind ein [sic!] für nicht korrigierte Schreibfehler oder auch <note> beliebiger Text\nmit optionalem Zeilenumbruch</note> zur Kennzeichnung handschriftlicher Ergänzungen.
Gibt es einen Weg diese Zeilen dennoch mit den anderen im 'Counter' zu vergleichen? Oder muss ich da händische zuordnen?
Wen das Wort nicht schlägt, den schlägt auch der Stock nicht.
Antworten