Letzte zeile einer Textdatei printen lassen?

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.
Teabag
User
Beiträge: 81
Registriert: Sonntag 13. Mai 2007, 20:44

Hallo Leute
Ich hätte da mal ne symple Frage.
Ich will die letzte zeile in einer Textdatei printen lassen,
wie geht denn das?
ich hatte bisher dass hier:

Code: Alles auswählen

write = open("text.txt","w")
dann soll der so was in der art machen:
print write (nur halt soll der nicht die datei printen sondern den text
und zwar davon nur die letzte Zeile.
Ich danke euch schon im voraus!

Euer Teabag
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hallo Teabag, willkommen im Forum,
Teabag hat geschrieben:ich hatte bisher dass hier:

Code: Alles auswählen

write = open("text.txt","w")
Du öffnest eine Datei zum Schreiben. Aaaaha. Es wäre zu überlegen ob man die Datei nicht besser im Lesemodus öffnet. Der Lesemodus ist übrigens "r".
Die Zeilen einer Datei bekommst du in einer Liste mit ``write.readlines()`` Und das letzte Element einer Liste hat den Index -1. Ich denke, damit hättest du alles zusammen, was nötig ist, dieses Problem selbst zu lösen. EIne ganze Lösung poste ich mit Absicht nicht - es wäre zu banal und du könntest nicht selber draufkommen.
Na dann mal frohes Schaffen - bei Fragen kannst du dich ja immer noch melden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

zurückgezogen wegen schneller antwort von Leonidas ;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sr4l hat geschrieben:**postponed**
Danke.

Apropos optimaler Lösung (ich habe unabhängig von dir dem OP ja das selbe Vorgehen empfohlen, welches du implementiert hast). Die würde wohl eher darin zu bestehen, die Datei Zeile für Zeile durchzugehen, wobei die aktuelle Zeile in einer temporären Variablen gespeichert werden würde. Wen man durch die Datei komplett durchiteriert ist, sollte die letzte zeile in der temporärern Variablen zu finden sein. Ich würde sagen, die Laufzeit wäre ``O(n)``, aber das ist sie so oder so.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Ich hätte da auch noch eine Lösung. Sie ist sicher nicht ideal und vielleicht auch nicht super durchdacht, aber sie ist schnell.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import time


FILENAME = "hallo.txt"


def fill_file(filename, lines_count):
    f = file(filename, "w")
    for i in xrange(lines_count):
        f.write("Das ist die perfekte Welle. Ich bin die Zeile mit der Nummer %i!\n" %i)
    f.close()


def get_last_line(filename):
    line = []
    f = file(filename, "rU")
    try:
        try:
            f.seek(-1, 2)
        except IOError:
            return None
        
        while True:
            char = f.read(1)
            if not char:
                break
            if line and char == "\n":
                break
            if char != "\n":
                line.append(char)
            try:
                f.seek(-2, 1)
            except IOError:
                break
    finally:
        f.close()
    return "".join(line[::-1])


for lines_count in (100000, 500000, 1000000, 10000000):
    old = time.time()
    fill_file(FILENAME, lines_count)
    new = time.time()
    print "Lines: %i" % lines_count
    print new - old
    
    old = time.time()
    print repr(get_last_line(FILENAME))
    new = time.time()
    print new - old
    
    print

Code: Alles auswählen

Lines: 100000
0.375
'Das ist die perfekte Welle. Ich bin die Zeile mit der Nummer 99999!'
0.0

Lines: 500000
3.17199993134
'Das ist die perfekte Welle. Ich bin die Zeile mit der Nummer 499999!'
0.0

Lines: 1000000
6.90600013733
'Das ist die perfekte Welle. Ich bin die Zeile mit der Nummer 999999!'
0.0

Lines: 10000000
51.8599998951
'Das ist die perfekte Welle. Ich bin die Zeile mit der Nummer 9999999!'
0.0
mfg
Gerold
:-)

PS: Hallo Teabag! Willkommen im Python-Forum!
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

gerold hat geschrieben:Ich hätte da auch noch eine Lösung.
Was ähnliches wollte ich auch vorschlagen, hast du aber nun schon gemacht ;)

Ich frage mich allerdings wie effektiv ein f.read(1) ist. Wird wirklich ein Zeichen aus der Datei gelesen? Da hängen doch sicherlich Puffer dazwischen, oder? Zumindest das Betriebsystem wird direkt einen ganzen Sektor lesen lassen, oder nicht? Von wegen "read-ahead", wobei das ja eigentlich "vorrauslesen" der Daten ist und nicht umgekehrt.

Die Frage ist also, ob es nicht Sinn macht einen ganzen rutsch an Daten vom Ende aus zu lesen... z.B.: Die letzten 3x80 = 240Zeichen oder so...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Da hängt ein Puffer dazwischen, sowohl der Blockpuffer vom Betriebssystem, das geht ja auch gar nicht anders weil man Daten nur in Blöcken von der Platte bekommt, als auch einer in der C-Laufzeitbibliothek.

Effizient ist es aber natürlich trotzdem nicht, weil man für jedes Byte einen "langsamen" Funktionsaufruf hat. Das würde schon in C ziemlich ins Gewicht fallen, bei Python sogar erheblich.

Andererseits ist es sicher schnell genug, solange man das nicht über hunderte von Dateien in einer Schleife macht und die letzte Zeile eine "vernünftige" oder "normale" Länge besitzt.
lunar

Eine Runde Nachsitzen für alle, die hier schon wieder anderen Leuten die Hausaufgaben gemacht haben :twisted:
BlackJack

Hat doch keiner wirklich gemacht. Und wenn gerold's Lösung abgegeben wird, dann fragt der Lehrer sicher nach. Falls der OP dann erklären kann, was der Code genau macht, hat er IMHO was gelernt und genügend eigene Leistung gezeigt.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Um nicht alle Zeilen in den Speicher laden zu müssen, wenn ohnehin nur die letzte relevant ist, würde ich zunächst versuchen, ans Dateiende zu springen und von da sukzessive (nicht zwingend zeichenweise) weiter vorne zu lesen, bis ein Zeilenumbruch gefunden ist. IIRC ist im ASPN-Cookbook auch ein Rezept um eine Datei rückwärts zu lesen.

Eleganter als f.readlines()[-1] ist es natürlich bei weitem nicht.
lunar

Mir ist gerade noch das linecache Modul eingefallen:

Code: Alles auswählen

import linecache
linecache.getlines(filename)[-1]
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

lunar hat geschrieben:Eine Runde Nachsitzen für alle, die hier schon wieder anderen Leuten die Hausaufgaben gemacht haben :twisted:
;)

linecache.getlines() ist mal dezent nicht in den Python-Docs. Skandal!
BlackJack

Ist ja auch nicht Teil der offiziellen API. Sollte man also mit Vorsicht geniessen.

Besonders effizient ist es auch nicht, weil die komplette Datei im Speicher bleibt, bis man mal `clearcache()` aufruft.
lunar

getlines ist doch mit einem Docstring gut dokumentiert. Die "offizielle" Funktion "getline" dagegen bietet noch nicht mal einen Docstring.

Die offizielle API des linecache Moduls sieht plötzlich ganz anders aus, wenn man nicht die Python-Doku öffnet, sondern in ipython die Quellen und die Docstrings anschaut (der Fragezeichen-"Operator" ist schon ne feine Sache ;) ). So gesehen ist "offizielle API" eine recht schwammige Sache.

Wobei ich dir im Bezug auf die Effizienz dieser Methode recht geben muss. Dafür ist der Code aber schön kurz ;)
BlackJack

Dann gib in IPython mal zwei Fragezeichen für's Modul ein:

Code: Alles auswählen

Type:             module
Base Class:       <type 'module'>
String Form:   <module 'linecache' from '/usr/lib/python2.4/linecache.pyc'>
Namespace:        Interactive
File:             /usr/lib/python2.4/linecache.py
Source:
"""Cache lines from files.

This is intended to read lines from modules imported -- hence if a filename
is not found, it will look down the module search path for a file by
that name.
"""

import sys
import os

__all__ = ["getline", "clearcache", "checkcache"]
# ...
Alles was nicht in der Doku und in `__all__` enthalten ist, würde ich als Implementationsdetail betrachten.
Teabag
User
Beiträge: 81
Registriert: Sonntag 13. Mai 2007, 20:44

Hallo
Vielen Dank für die vielen antworten, bin grad schon ab rumprobieren

Gruß Teabag
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Hat doch keiner wirklich gemacht. Und wenn gerold's Lösung abgegeben wird, dann fragt der Lehrer sicher nach. Falls der OP dann erklären kann, was der Code genau macht, hat er IMHO was gelernt und genügend eigene Leistung gezeigt.
Stimmt.

Ich gebe zu, im habe mir eine Lösung ähnlich zu gerolds im Kopf rumgehen lassen, also eine Datei von "hinten" nach Newlines abzusuchen, aber letztenlich fand ich, dass es sich nicht ohnt sowas zu implementieren. Zumindest nicht bei der Größe der meisten Dateien, sofern das überhaupt Vorteile bringt.
Man könnte es aber auch modifizieren, dass es genau einen Block am Stück liest und diesen Durchsucht. Wobei man dann wissen müsste wie groß so ein Block ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
thelittlebug
User
Beiträge: 188
Registriert: Donnerstag 20. Juli 2006, 20:46
Wohnort: Wien
Kontaktdaten:

Ich hab warscheinlich die feigste Lösung :)

Code: Alles auswählen

import commands

meinedatei = "/var/log/messages"
meinezeile = commands.getoutput("tail -n 1 %s" % meinedatei)
print meinezeile
lgherby
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackJack hat geschrieben:

Code: Alles auswählen

__all__ = ["getline", "clearcache", "checkcache"]
Alles was nicht in der Doku und in `__all__` enthalten ist, würde ich als Implementationsdetail betrachten.
Hallo BlackJack!

Ich habe mir "linecache.py" durchgelesen und habe das Gefühl, dass einfach nur vergessen wurde, "getlines" in "__all__" rein zu schreiben. Vielleicht hat der Programmierer auch nur gedacht, dass "getlines" niemand brauchen wird. Ich sehe hier aber keinen Grund, weshalb man es nicht verwenden sollte. :K Habe ich etwas übersehen?

Außerdem finde ich, dass "getline" komisch ist. :roll: Warum wird nicht 0 als erste Zeilennummer verwendet? Warum wird nicht zugelassen, dass auf die Liste mit den Zeilen per "slicing" zugegriffen werden kann? Das ganze Modul fühlt sich irgendwie komisch an.

Edit:

Wie ich aus der History ersehe, hat "montanaro" erst neun Jahre nach Erstellen des Moduls "__all__" nachgetragen. Ich glaube also nicht, dass es im Sinne des Erfinders ist, dass "getlines" nicht benutzt werden soll.

Siehe: http://svn.python.org/view/python/trunk ... 2&view=log

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
lunar

gerold hat geschrieben:Das ganze Modul fühlt sich irgendwie komisch an.
Liegt wohl daran, dass es eine eher interne Sache ist, die vor allem für die Kontextzeilen bei Tracebacks benötigt wird...
Antworten