Regular Expression - re.finditer oderverknüpfte Suche

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.
Antworten
19sanjok88
User
Beiträge: 1
Registriert: Freitag 18. März 2022, 13:57

Hallo zusammen,

nachdem ich mich jetzt erfolglos mit meiner Aufgabe versucht habe, hoffe ich dass mir hier jemand helfen kann. Ich habe folgendes Problem. Ich will ein PDF Text auslesen, dessen Kapitel wie folgt unterteilt ist:

(1)
.....
(1.1)
.....
(1.1.1)
..... usw.

Das Auslesen der PDF und finden der Einzelnen Kapiteln ist kein Problem, damit komme ich klar.

Sobald ich den Textinhalt der PDF in ein String umgewandelt habe, versuche ich mit folgender Methode die einzelnen Kapitel zu finden:

count = 0

for match in re.finditer(r"\n\((\d\)|\n\((\d.\d\)|\n\((\d.\d.\d\)", String):
count += 1
print("match", count, match.group(), "start index", match.start(), "End index", match.end(),)

Was ich mich noch frage ist: Gibt es auch eine Möglichkeit herauszufinden welche dieser drei Bedingungen erfüllt wurde? Ausgehend davon würde ich gerne unterschiedliche Operationen durchführen.

Ich hoffe ihr könnt mir weiterhelfen.

Vielen Dank und viele Grüße
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

Ich glaube deine Regex ist fehlerhaft, mir werden jedenfalls ein paar fehlende Klammern anzeigt.

Abgesehen davon sind Strings eher ungeeignet dort ganze PDFs zu speichern, solange dir der Speicher nicht ausgeht, mag das vielleicht noch gehen. Aber vertraue mir, wenn ich dir verspreche, dass es zu komischen Effekten führen kann, wenn die Strings mehre MB haben.
Besser du überlegst dir gleiche eine Repräsentation deiner Datei, bei der du immer nur ein teil im Rahm halten musst. Vielleicht eine Art Stream. Generell, dein Code besser in Code Tags setzen, das macht es uns leichter den Code zu lesen. Außerdem, Variablen werden in Python kleingeschrieben, es muss also string heißen.

Ich würde die Regex leicht um schreiben um dann in der Match-Gruppe einfach die Punkte zahlen, dann weiß ich welcher Fall es ist. Last but bot least meistens muss man in Python nicht manuell Zähler inkrementieren, dafür gibt es selten Grund in dein Fall ist enumerate dein Freund.

Code: Alles auswählen

 string = """
 (1)
 (1.1)
 (1.1.1)
 """
 
regex = re.compile(r"^\((?P<chapter>(\d+\.{0,1})+)\)", re.MULTILINE)

for cnt, match in enumerate(re.finditer(regex, string)):
      chapter = match.group('chapter')
      print(f"Chapter No: {cnt} match: {chapter} Numbers of dot : { chapter.count('.')}")
Sirius3
User
Beiträge: 17761
Registriert: Sonntag 21. Oktober 2012, 17:20

@imonbln: ganz abgesehen davon, dass es sich hier nicht um PDFs handelt, sondern um den Textinhalt eines PDFs, der Speicher heutzutage ist groß genug dafür.
Auch wenn Strings mehrere MB haben, welche komischen Effekte meinst Du?
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

Super aussage, Speicher ist billig, lass ihn uns unnötigerweise verschwenden. Nach der Logik dürfte es auch keine Generatoren in Python geben, denn der Speicher ist groß genug dafür, alles im RAM zu halten.

Es gibt nicht nur Server mit TB an RAM es gibt auch Embedded Devices und ich habe schon zu oft gesehen, dass ein Entwickler mit der Einstellung, ich habe genügend Speicher, ich kann eine ganze Dissertation im einen String packen, das System grundlos in den Boden gerammt hat.
Die komischen Effekte von den Spreche sind weitreichend (sonst währen sie ja nicht komisch), erlebt habe ich schon, dass Dein Script lange nicht reagiert, weil es denn String zum Beispiel mit Count oder Replace durchsucht (gerne bis zu mehren Minuten), dass der OOM Killer das Ding einfach abschießt, weil es einfach zu viel vom gesamten RAM haben will.
Oder dass ein Programm krachen geht, weil der Kernel Speicher zugesichert hat, denn es nicht mehr gibt im System, zugegeben war eigentlich ein Linux bug in malloc, wurde aber getriggert von einem Programm wo der Entwickler sich auch keine Gedanken um Memory Management gemacht hat.

Auch anders beliebtes Antipattern aus dem Python Umfeld ist bla = fin.read(), geht genauso lange gut wie der Read genügend Speicher hat, aber wenn nicht, ist es undefiniert und damit komisch.

Siehe hierzu z. B. https://docs.python.org/3/tutorial/inpu ... le-objects
To read a file’s contents, call f.read(size), which reads some quantity of data and returns it as a string (in text mode) or bytes object (in binary mode). size is an optional numeric argument. When size is omitted or negative, the entire contents of the file will be read and returned; it’s your problem if the file is twice as large as your machine’s memory.
Ich habe mehr als ein Embedded Device gesehen, wo genau das, das Problem war und deshalb ein Update nicht geklappt hat.

Ergo auch Python kann der Speicher ausgehen und in einen String mehre MB Payload zu packen ist, ein guter Anfang solche Probleme schnell praktisch kennen zulernen. Auch wenn es vermutlich oft auf dem Entwickler PC gut geht, ein Bewusstsein für solche Probleme sollte man auch heute noch mitbringen, wo Speicher billig ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13123
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@imonbln: Ich bin ja auch dafür nicht unnötig verschwenderisch mit Ressourcen umzugehen, aber wenn jemand den Text aus PDF-Dateien extrahieren möchte, sind wir in aller Regel a) nicht mehr im Embedded-Bereich, b) bei einer Programmiersprache die alleine für das ausführen von einer leeren Datei schon 20+ MiB RAM belegt, und c) damit dafür schon mehr als der Textinhalt von den allermeisten PDFs verbraucht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Statt \d würde ich auf jeden Fall \d+ empfehlen. Damit matcht man ein oder mehrere Ziffern. Sonst kommst du nämlich nicht über Kapitel 9 hinaus.

Dann könnte man alternativ zum Oder-Ausdruck auch sagen, dass es sich um eine beliebig lange Folge von Ziffern mit dem Punkt als Trenner handelt. Da das unterste Kapitel keinen Punkt am Ende stehen hat, würde man den optional setzen via Fragezeichen (= 0 oder 1 Treffer).

Der Teil wäre somit: \d+\.?
Für mehrere Vorkommen nutzt man das Plus und muss den Ausdruck dafür umklammern: (\d+\.?)+

Zusätzlich noch mit dem Escapen der Klammern im Text: \((\d+\.?)+\)

Und wenn ich jetzt an die Kapitel ohne Klammern will, muss ich eine innere Gruppe festlegen. Das geht auch wieder mit Klammern, nur eben aus Regex-Sicht: \(((\d+\.?)+)\)

Jetzt kann ich auch einfach match() benutzen und ziehe mir die innere Gruppe:

Code: Alles auswählen

print(re.match(r"\(((\d+\.?)+)\)", "(13.1.5)").group(1))
Und die würde ich dann wohl stumpf via map(int, result.split(".")) mit den Bordmitteln von Python verarbeiten.

Die Tiefe der Kapitel-Angabe kriegt man z.B. mit result.count("."), was ja im Prinzip auch schon erwähnt wurde.
Antworten