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

Sonntag 13. Mai 2007, 20:48

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 13. Mai 2007, 21:16

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 Modvoice
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Sonntag 13. Mai 2007, 21:17

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

Sonntag 13. Mai 2007, 21:24

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 Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sonntag 13. Mai 2007, 22:22

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
Moderator
Beiträge: 8483
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Montag 14. Mai 2007, 07:37

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...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Montag 14. Mai 2007, 09:11

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

Montag 14. Mai 2007, 09:53

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

Montag 14. Mai 2007, 10:40

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

Montag 14. Mai 2007, 11:03

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

Montag 14. Mai 2007, 14:23

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

Montag 14. Mai 2007, 14:34

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

Montag 14. Mai 2007, 14:39

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

Montag 14. Mai 2007, 14:52

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

Montag 14. Mai 2007, 15:28

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.
Antworten