Durch bestimmte Textsegmente iterrieren

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
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Hallo,

gibt es in Python eine Möglichkeit, ohne reg expressions anzuwenden, bestimmte Segmente eines Textes auszuslesen. Eine Textdatei bestehe beispislweise aus folgenden Inhalt:

Code: Alles auswählen

segmentA {
   content Aa
   content Ab
   content Ac
    ....
}

segmentB {
   content Ba
   content Bb
   content Bc
   ......
}

segmentC {
  content Ca
  content Cb
  content Cc
  ......
}
Wie sage ich dem Script jetzt, suche nach einem bestimmten String innerhalb des Textabschnitt das mit "SegmentC {" beginnt und mit "}" endet.

Eine idee?

Danke im vorraus!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Laufe über die Zeilen des Textes. Wenn du auf den Marker für den Beginn des Textabschnitts stößt, dann merke dir, dass du ab jetzt im Suchmodus bist. Wenn du auf den Marker für das Ende des Textabschnitts stößt, dann schalte den Suchmodus wieder ab. Das Suchen selber ist ja trivial (if searchtext in line).
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Hallo,
danke erstmals, ich habe das script jetzt so aufgebaut:

Code: Alles auswählen

def SearchStuff(lines,sstr):
    i=0
    while(lines[i]!='}'):
        #Suche einen bestimmten String in den lines. Z.b: nach dem String "Ca"  
         if 'Ca' in lines[i]:
            return lines[i]
         i+=1

def main(search_str):
    f=open('file.txt','r')
    lines = f.readlines()
    f.close()
    for line in lines:
        if search_str in line:
            index = lines.index(line)
            break
    lines = lines[index+1:]
    print SearchStuff(lines,search_str) 

search_str = 'segmentC {'   
main(search_str)
Das Problem hierbei ist: Sobald der Parser auf die Linie mit dem String "Ca" stösst, gibt er auch nur diese Linie aus. Im konkreten Beispiel würde das bedeuten (angenommen "segmentC {" wurde erkannt):

Code: Alles auswählen

segment C {
 content Ca\something_ABC\something_EFG\
 content Ca\something_XYZ\something_UVW\
 content Cc\
}
Hierbei gibt er mir nur die Linie: "content Ca\something_ABC\something_EFG\" aus. Ich bräuchte aber auch die untere Linie, da diese auch den String "Ca" enthält.

Eine Idee?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

milexy86 hat geschrieben:Hierbei gibt er mir nur die Linie: "content Ca\something_ABC\something_EFG\" aus. Ich bräuchte aber auch die untere Linie, da diese auch den String "Ca" enthält.
Du beendest die Verarbeitung mit "return" sobald du eine passende Zeile gefunden hast.

Ich würde so etwas vorschlagen:

Code: Alles auswählen

def wanted_lines(fp, search_text, start_marker, end_marker='}'):
    wanted = False
    for line in fp:
        line = line.rstrip()  # das könnte man auch noch in einen Generator eine Zeile höher packen
        if line == start_marker:
            wanted = True
        elif line == end_marker:
            wanted = False
        else:
            if wanted and search_text in line:
                yield line


def main():
    start_text = 'segment C {'
    end_text = '}'
    search_text = 'Ca'

    with open('file.txt', 'r') as fp:
        for line in wanted_lines(fp, search_text, start_text, end_text):
            print(line)


if __name__ == '__main__':
    main()
Dieser Code liest auch nicht die komplette Datei auf einmal in den Speicher sondern arbeitet sie der Reihe nach ab.
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Vielen Dank!

Sollte de start_marker auch nicht entsprechend initialisiert werden?

Wenn ich das Script in der konsole ausführe, tut es leider nichts, gibt auch keine syntax fehler :K
BlackJack

@milexy86: Für `start_marker` wird doch ein Wert übergeben, also verstehe ich die Frage nicht?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier noch eine Variante, die vorab geschweifte Klammern annimmt und folglich nur den reinen Namen der Section benötigt. Zudem wird bloß der erste Treffer ausgegeben anstatt eines Iterators:

Code: Alles auswählen

TEST_TEXT = """\
segmentA {
   content Aa
   content Ab
   content Ac
    ....
}
 
segmentB {
   content Ba
   content Bb
   content Bc
   ......
}
 
segmentC {
  content Ca
  content Cb
  content Cc
  ......
}"""

def search(lines, section_name, text, start_mark='{', end_mark='}'):
    inside_section = False
    for line in lines:
        line = line.strip()
        if line.endswith(start_mark):
            current_section = line.rstrip(start_mark).strip()
            if current_section == section_name:
                inside_section = True
        elif line == end_mark:
            inside_section = False
        elif inside_section and text in line:
            return line


def main():
    lines = TEST_TEXT.splitlines()
    print(search(lines, 'segmentC', 'Cb'))


if __name__ == '__main__':
    main()
Anstatt einer Liste von Zeilen funktioniert dies natürlich auch mit einem Dateiobjekt.
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Hallo,

danke für diesen Vorschlag! Eine Dumme Frage:

Was hat sich mit dem TEST_TEXT = """\ in der ersten Zeile auf sich? Ist das so ne Art Marker im Text File das durchsucht wird?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

milexy86 hat geschrieben:Hallo,

danke für diesen Vorschlag! Eine Dumme Frage:

Was hat sich mit dem TEST_TEXT = """\ in der ersten Zeile auf sich? Ist das so ne Art Marker im Text File das durchsucht wird?
Nein, das ist der Beginn einer ganz normalen String-Definition. Du kannst mehrzeiligen Text definieren indem du ihn in dreifache Anführungszeichen einschließt. Der Backslash am Ende ist davon unabhängig. Dieser dient dazu Zeilen fortzuführen und verhindert hier eine leere Zeile zu Beginn des Textes (und wäre nicht nötig gewesen).

Code: Alles auswählen

>>> x = """
a
b
c"""
>>> x
'\na\nb\nc'
>>> x = """\
a
b
c"""
>>> x
'a\nb\nc'
Sofern nicht bekannt: Das "\n" in der Ausgabe steht für einen Zeilenumbruch.
Rigoletto
User
Beiträge: 28
Registriert: Freitag 14. Februar 2014, 21:05

Nur so als Tip, wenn das Textformat nicht festgelegt ist, wechsel ggf. auf XML.
Das

Code: Alles auswählen

import xml.etree.ElementTree as ET
lässt sich echt einfach nutzen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Oder besser JSON, oder INI, oder YAML, oder... ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Letztlich stellt sich natürlich auch die Frage, wieso das nicht gleich alles komplett in Python oder kmplett in PHP erledigt wird...
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

@/me In dem Text file das so aufgebaut ist wie anfangs beschrieben, habe ich nur leserechte...d.h. ich kann dort nicht einfach string definitionen einbauen.
@snafu: Wird doch alles auch nur in python gemacht :wink:

Ganz konkret, sieht das text file so aus:

source_code_directories {
  • c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_X
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_Y
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_Z
    c:\Folder_A\Folder_B\Project_Sources\ZZ\Configuration\
    c:\Folder_A\Folder_B\Project_Sources\ZZ\Testing
    c:\Folder_A\Folder_B\Project_Sources\ZZ\Test_Outputs
    c:\Folder_A\Folder_B\Configuration\Gener_Sourcess\X
}

source_code_includes {
  • c:\Folder_A\Folder_B\Configuration\Gener_Sourcess\X
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_X
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_Y
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_Z
    c:\Folder_A\Folder_B\Project_Sources\Algorithm\Algorithm_F
    c:\Folder_A\Folder_B\Project_Sources\FF\Test_Inputs
    c:\Folder_A\Folder_B\Project_Sources\FF
    c:\Folder_A\Folder_B\Libraries\Includes_A
    c:\Folder_A\Folder_B\Libraries\Includes_B
}

Jetzt möchte ich also ganz einfach sagen: Gebe mir alle Zeilen aus die den String "\Algorithm" innerhalb "source_code_includes {" enthalten.
Die Lösung von @/me habe ich ausprobiert. Es gibt keine Syntax fehler, aber das output file bleibt auch leer. :K
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

milexy86 hat geschrieben:@/me In dem Text file das so aufgebaut ist wie anfangs beschrieben, habe ich nur leserechte...d.h. ich kann dort nicht einfach string definitionen einbauen.
Das sollst du ja auch gar nicht. Lerne Code zu lesen und zu verstehen. snafu hat hier ein Beispiel für die mögliche Verarbeitung gegeben. In diesem Beispiel wurde der Text als String definiert und nicht aus einer Datei gelesen. Wenn du den Text aus einer Datei haben möchtest, dann musst du das schon selber passend umsetzen.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

milexy86 hat geschrieben:Die Lösung von @/me habe ich ausprobiert. Es gibt keine Syntax fehler, aber das output file bleibt auch leer. :K
Wenn ich in meinem Code die passenden Angaben mache, dann funktioniert das problemlos.

Code: Alles auswählen

    start_text = 'source_code_includes {'
    search_text = '\\Algorithm'
Du hast nicht vielleicht in der Datei noch führende Leerzeichen vor den Zeilen? Lass dir doch die Zeilen bei der Verarbeitung mal ausgeben (vorzugsweise mit print(repr(line)) ) und schau mal wie die Zeile aussieht die eigentlich gefunden werden sollte, aber nicht gefunden wird.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

milexy86 hat geschrieben:@snafu: Wird doch alles auch nur in python gemacht :wink:
Ups, ich ich bin mit einem anderen Thread durcheinander gekommen... :oops:
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Hallo,

ja, es funktioniert jetzt!

Wie kann ich aber einen Tuple an strings suchen, also beispielsweise:

Code: Alles auswählen

search_text = '\\Algorithm', '\\Libraries'
Muss ich im Attribut "search_text" bei der Funktion "wanted_lines" diese Möglichkeit irgendwie explizit angeben?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Passe Zeile 10 (Code von /me) an mit

Code: Alles auswählen

any(needle in line for needle in search_text)
(und am besten aenderst du `search_text` noch zum Plural)

Die Alternative sind Regular Expressions.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

milexy86 hat geschrieben:Wie kann ich aber einen Tuple an strings suchen, [...]
Wenn du ein Tupel aus Möglichkeiten definierst, dann musst du überprüfen ob mindestens einer der Einträge in der Zeile zu finden ist. Zuerst nennen wir den Bezeichner mal search_texts. Das hat zwar keine Auswirkungen auf den Ablauf des Codes, beschreibt den Inhalt aber korrekter.

Um abzufragen ob eine der Möglichkeiten passt kann man any verwenden. any erhält als Parameter ein Iterable dessen zurückgelieferte Elemente überprüft werden. Diese Elemente sollten also angeben, ob die Suche zutrifft. Benötigt wird also eine Schleife die alle Elemente von search_texts durchläuft und für jedes zurückmeldet ob es in line vorhanden ist. Wenn man das kombiniert gelangt man zu folgendem Code:

Code: Alles auswählen

if wanted and any(s in line for s in search_texts):
Solltest du Verständnisprobleme mit der Zeile haben, dann frag bitte nach. Code nach diesem Muster ist ziemlich elementar beim Umgang mit Python.

Edit: Wenn hier zwei erfahrene Leute den gleichen Code vorschlagen, dann ist das vermutlich korrekt. :D
milexy86
User
Beiträge: 22
Registriert: Freitag 7. Februar 2014, 16:33

Hallo,

danke euch nochmals für die umfangreiche Unterstützung und die Tipps. Bin jetzt um einiges an Wissen reicher geworden :)
Die Lösung mit "any" funktioniert.

Ich habe das auch mit regulären ausdrücken gelöst:

Code: Alles auswählen

import re
with open("input_file.txt", "r") as infile, open("output_file.txt", 'w') as outfile: 

	for result in re.findall('source_code_includes(.*?)\}', infile.read(), re.S):
		for line in result.split('\n'):
			if re.search('(Algorithm|Libraries)', line):
				outfile.write(line + '\n')
Antworten