Seite 1 von 2

Letzte zeile einer Textdatei printen lassen?

Verfasst: Sonntag 13. Mai 2007, 20:48
von Teabag
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

Re: Letzte zeile einer Textdatei printen lassen?

Verfasst: Sonntag 13. Mai 2007, 21:16
von Leonidas
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.

Verfasst: Sonntag 13. Mai 2007, 21:17
von Sr4l
zurückgezogen wegen schneller antwort von Leonidas ;-)

Verfasst: Sonntag 13. Mai 2007, 21:24
von Leonidas
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.

Verfasst: Sonntag 13. Mai 2007, 22:22
von gerold
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!

Verfasst: Montag 14. Mai 2007, 07:37
von jens
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...

Verfasst: Montag 14. Mai 2007, 09:11
von 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.

Verfasst: Montag 14. Mai 2007, 09:53
von lunar
Eine Runde Nachsitzen für alle, die hier schon wieder anderen Leuten die Hausaufgaben gemacht haben :twisted:

Verfasst: Montag 14. Mai 2007, 10:40
von 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.

Verfasst: Montag 14. Mai 2007, 11:03
von Y0Gi
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.

Verfasst: Montag 14. Mai 2007, 14:23
von lunar
Mir ist gerade noch das linecache Modul eingefallen:

Code: Alles auswählen

import linecache
linecache.getlines(filename)[-1]

Verfasst: Montag 14. Mai 2007, 14:34
von Y0Gi
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!

Verfasst: Montag 14. Mai 2007, 14:39
von 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.

Verfasst: Montag 14. Mai 2007, 14:52
von 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 ;)

Verfasst: Montag 14. Mai 2007, 15:28
von 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.

Vielen Dank für die vielen Antworten

Verfasst: Montag 14. Mai 2007, 15:40
von Teabag
Hallo
Vielen Dank für die vielen antworten, bin grad schon ab rumprobieren

Gruß Teabag

Verfasst: Montag 14. Mai 2007, 18:26
von Leonidas
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.

Verfasst: Montag 14. Mai 2007, 18:30
von thelittlebug
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

Verfasst: Montag 14. Mai 2007, 19:31
von gerold
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
:-)

Verfasst: Montag 14. Mai 2007, 20:17
von 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...