.txt Datei einlesen und bestimmte Zeile finden

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
Benutzeravatar
Ronsen90
User
Beiträge: 19
Registriert: Montag 22. Juni 2015, 10:46
Wohnort: Leipzig und Neustrelitz

Hallo liebe Gemeinde,

ich möchte, dass mein Programm eine .txt-Datei einliest und nach einer bestimmten Zeile durchsucht (im hiesigen Beispiel steht in der Zeile x). Sobald es diese gefunden hat, soll es deren Zeilennummer ausgeben.

Wie zu erwarten war, funktioniert mein kleines Programm nicht so wie ich will, aber es wird auch kein Fehler ausgegeben...

Code: Alles auswählen

with open("test.txt", "r") as f:
    Counter = 0
    for row in f:
        Counter = Counter + 1
        if row == "x":
            break
    print Counter
Für ein paar Tipps wäre ich dankbar.
BlackJack

@Ronsen90: In der Zeile in der Datei steht nicht nur ein 'x' sondern sehr wahrscheinlich auch das Zeilenendezeichen. Das gehört mit zur Zeile. Einzige Ausnahme wäre wenn man das 'x' in der letzten Zeile stehen hat und die nicht mit einem Zeilenendezeichen abgeschlossen wurde.

Statt den `Counter` manuell zu pflegen, würde sich die `enumerate()`-Funktion anbieten.

Edit: Ungetestet:

Code: Alles auswählen

from __future__ import absolute_import, division, print_function


def main():
    with open('test.txt', 'r') as lines:
        print(
            next(
                (i for i, line in enumerate(lines, 1) if line.rstrip() == 'x'),
                0
            )
        )


if __name__ == '__main__':
    main()
Benutzeravatar
Ronsen90
User
Beiträge: 19
Registriert: Montag 22. Juni 2015, 10:46
Wohnort: Leipzig und Neustrelitz

Hey BlackJack,

spitze, der Code funktioniert. Ich habe jedoch Probleme, ihn für mich zu verinnerlichen.
Also def kenne ich schon, damit kreierst du eine Funktion, die dann über main() ganz am Ende ausgeführt wird.
Ich verstehe nicht so richtig, wie der next-Befehl funktioniert. Ich habe dazu nur englische Erklärungen gelesen, aus denen ich nicht wirklich schlau werde.

"Retrieve the next item from the iterator by calling its next() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised."

Eine Iteration ist doch eine Wiederholung. Was wird da genau iteriert?
Genauso kann ich mit diesem i for i nichts anfangen...
Könntest du die Zeilen 7 bis 9 bitte noch mal für Dummies erklären?
BlackJack

@Ronsen90: Zeile 8 ist ein Generatorausdruck. Der funktioniert im Grunde wie eine „list comprehension” (LC) nur dass er keine komplette Liste mit allen Elementen ergibt, sondern einen Iterator bei dem die Elemente erst ausgewertet werden wenn man danach fragt, also wenn man beispielsweise mit der `next()`-Funktion das nächste Element abfragt, oder in einer Schleife über den Generator itertiert.

Wenn Du da ein ``i for i`` liest, dann ist das nicht weit genug gelesen, denn das ist ja nur der Anfang. Das Komma trennt nicht ``i for i`` vom Rest sondern das Komma steht zwischen `i` und `line`! Wird vielleicht deutlicher wenn man es klammert:

Code: Alles auswählen

(i for (i, line) in enumerate(lines, 1) if line.rstrip() == 'x')
Der Ausdruck liefert alle `i`\s für Zeilen die wenn man Whitespace am Ende entfernt nur noch aus einem 'x' bestehen.

Mal Schritt für Schritt erst mit einer LC damit man potentiell alle Werte sieht wo beim Generator in dem Code nur bis zum ersten Treffer ausgewertet wird. Nehmen wird das Wort 'mississippi' und wollen wissen wo das erste 'i' vorkommt, wobei die Zählung der Buchstaben bei 1 anfangen soll. Wir nummerieren als erstes mal die Buchstaben mit der `enumerate()`-Funkion und brauchen da schon etwas um die Werte auch tatsächlich alle zu erzeugen, denn `enumerate()` liefert auch erst einmal nur einen Iterator und erzeugt die einzelnen Elemente erst wenn man sie abfragt:

Code: Alles auswählen

In [1]: s = 'mississippi'

In [2]: enumerate(s, 1)
Out[2]: <enumerate at 0x93b2324>

In [3]: [(i, c) for i, c in enumerate(s, 1)]
Out[3]: 
[(1, 'm'),
 (2, 'i'),
 (3, 's'),
 (4, 's'),
 (5, 'i'),
 (6, 's'),
 (7, 's'),
 (8, 'i'),
 (9, 'p'),
 (10, 'p'),
 (11, 'i')]
Jetzt packen wir noch eine Bedingung dazu die anhand des aktuellen Buchstabens in `c` alles ausser 'i' filtert:

Code: Alles auswählen

In [4]: [(i, c) for i, c in enumerate(s, 1) if c == 'i']
Out[4]: [(2, 'i'), (5, 'i'), (8, 'i'), (11, 'i')]
Da uns nur die Stelle interessiert, aber nicht der Buchstabe, denn wir wissen ja dass der immer 'i' ist, lassen wir den Buchstaben weg und bekommen eine Liste mit den Stellen an denen ein 'i' steht:

Code: Alles auswählen

In [5]: [i for i, c in enumerate(s, 1) if c == 'i']
Out[5]: [2, 5, 8, 11]
Jetzt interessiert uns aber gar nicht jedes 'i' sondern nur das erste, darum ist das ungünstig wenn man eine Liste mit *allen* Werten erstellt. Wenn man die eckigen Klammern nun durch runde ersetzt, wird aus der LC ein Generatorausdruck der nicht alle Werte berechnet, sondern erst wenn man danach fragt. Und nach dem nächsten Wert, also beim ersten mal nach dem ersten Wert, kann man mit der `next()`-Funktion fragen:

Code: Alles auswählen

In [6]: next((i for i, c in enumerate(s, 1) if c == 'i'))
Out[6]: 2
Im Programm habe ich dann noch ein zweites Argument an `next()` übergeben. Das ist nötig falls man etwas filtert so dass am Ende gar keine Elemente von dem Generator geliefert werden, zum Beispiel wenn man nur Elemente aus `s` haben will die den Wert 'x' haben, die es aber gar nicht gibt:

Code: Alles auswählen

In [7]: next((i for i, c in enumerate(s, 1) if c == 'x'))
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-7-f71d39b4df0f> in <module>()
----> 1 next((i for i, c in enumerate(s, 1) if c == 'x'))
Statt der Ausnahme hätte ich in diesem Fall gerne den Wert 0, also gebe ich den als Defaultwert für diesen Fall an:

Code: Alles auswählen

In [8]: next((i for i, c in enumerate(s, 1) if c == 'x'), 0)
Out[8]: 0
Antworten