Dokumente für das io-Modul
with open(fname, 'rb') as fh:
first = next(fh).decode()
fh.seek(-1024, 2)
last = fh.readlines()[-1].decode()
Der variable Wert ist hier 1024: Er repräsentiert die durchschnittliche Zeichenfolgenlänge. Ich wähle zum Beispiel nur 1024. Wenn Sie eine Schätzung der durchschnittlichen Zeilenlänge haben, können Sie diesen Wert einfach mal 2 verwenden.
Da Sie keinerlei Ahnung von der möglichen Obergrenze für die Zeilenlänge haben, besteht die offensichtliche Lösung darin, die Datei zu durchlaufen:
for line in fh:
pass
last = line
Sie müssen sich nicht um das Binärflag kümmern, das Sie einfach verwenden könnten open(fname)
.
ETA : Da Sie viele Dateien bearbeiten müssen, können Sie ein Beispiel für ein paar Dutzend Dateien erstellen random.sample
und diesen Code ausführen, um die Länge der letzten Zeile zu bestimmen. Mit einem a priori großen Wert der Positionsverschiebung (sagen wir 1 MB). Auf diese Weise können Sie den Wert für den gesamten Lauf schätzen.
fh.seek(-1024, os.SEEK_END)
anstelle vonfh.seek(-1024, 2)
erhöht die Lesbarkeit.open(fname)
. Das Öffnen mit derb
Flagge ist entscheidend. Wenn Sieopen(fname)
stattopen(fname, 'rb')
Sie erhalten io.UnsupportedOperation: kann nicht von Null verschiedene End-Relativ sucht .Um sowohl die erste als auch die letzte Zeile einer Datei zu lesen, können Sie ...
readline()
, ...def readlastline(f): f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found ... f.seek(-2, 1) # ... jump back, over the read byte plus one more. return f.read() # Read all data from this point on. with open(file, "rb") as f: first = f.readline() last = readlastline(f)
Wechsel zu dem zweiten letzten Byte unmittelbar nachlaufZeilenUmbrüche zu verhindern Leerzeilen zu bewirken , zurückgeführt wird *.
Der aktuelle Offset wird jedes Mal, wenn ein Byte gelesen wird, um eins vorgeschoben, sodass der Rückschritt zwei Bytes gleichzeitig erfolgt, nach dem zuletzt gelesenen Byte und dem als nächstes zu lesenden Byte.
Der übergebene
whence
Parameter gibt an,fseek(offset, whence=0)
dassfseek
nach Positionsbytesoffset
relativ zu ... gesucht werden soll.0
oderos.SEEK_SET
= Der Anfang der Datei.1
oderos.SEEK_CUR
= Die aktuelle Position.2
oderos.SEEK_END
= Das Ende der Datei.* Wie zu erwarten ist, besteht das Standardverhalten der meisten Anwendungen, einschließlich
print
undecho
, darin, an jede geschriebene Zeile eine anzuhängen, und hat keine Auswirkung auf Zeilen, denen ein nachfolgendes Zeilenumbruchzeichen fehlt.Effizienz
Ich habe diese Methode zeitlich festgelegt und mit der Top-Antwort verglichen.
10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
Millionen von Zeilen würden den Unterschied viel mehr erhöhen .
Exakt-Code für das Timing:
with open(file, "rb") as f: first = f.readline() # Read and store the first line. for last in f: pass # Read all lines, keep final value.
Änderung
Eine komplexere und schwerer zu lesende Variante, um Kommentare und Probleme anzusprechen, die seitdem aufgeworfen wurden.
Fügt auch Unterstützung für Multibyte-Trennzeichen hinzu
readlast(b'X<br>Y', b'<br>', fixed=False)
.Bitte beachten Sie, dass diese Variation für große Dateien aufgrund der im Textmodus erforderlichen nicht relativen Offsets sehr langsam ist . Passen Sie es an Ihre Bedürfnisse an oder verwenden Sie es überhaupt nicht, da Sie es wahrscheinlich besser verwenden, wenn Sie
f.readlines()[-1]
Dateien im Textmodus öffnen.#!/bin/python3 from os import SEEK_END def readlast(f, sep, fixed=True): r"""Read the last segment from a file-like object. :param f: File to read last line from. :type f: file-like object :param sep: Segment separator (delimiter). :type sep: bytes, str :param fixed: Treat data in ``f`` as a chain of fixed size blocks. :type fixed: bool :returns: Last line of file. :rtype: bytes, str """ bs = len(sep) step = bs if fixed else 1 if not bs: raise ValueError("Zero-length separator.") try: o = f.seek(0, SEEK_END) o = f.seek(o-bs-step) # - Ignore trailing delimiter 'sep'. while f.read(bs) != sep: # - Until reaching 'sep': Read sep-sized block o = f.seek(o-step) # and then seek to the block to read next. except (OSError,ValueError): # - Beginning of file reached. f.seek(0) return f.read() def test_readlast(): from io import BytesIO, StringIO # Text mode. f = StringIO("first\nlast\n") assert readlast(f, "\n") == "last\n" # Bytes. f = BytesIO(b'first|last') assert readlast(f, b'|') == b'last' # Bytes, UTF-8. f = BytesIO("X\nY\n".encode("utf-8")) assert readlast(f, b'\n').decode() == "Y\n" # Bytes, UTF-16. f = BytesIO("X\nY\n".encode("utf-16")) assert readlast(f, b'\n\x00').decode('utf-16') == "Y\n" # Bytes, UTF-32. f = BytesIO("X\nY\n".encode("utf-32")) assert readlast(f, b'\n\x00\x00\x00').decode('utf-32') == "Y\n" # Multichar delimiter. f = StringIO("X<br>Y") assert readlast(f, "<br>", fixed=False) == "Y" # Make sure you use the correct delimiters. seps = { 'utf8': b'\n', 'utf16': b'\n\x00', 'utf32': b'\n\x00\x00\x00' } assert "\n".encode('utf8' ) == seps['utf8'] assert "\n".encode('utf16')[2:] == seps['utf16'] assert "\n".encode('utf32')[4:] == seps['utf32'] # Edge cases. edges = ( # Text , Match ("" , "" ), # Empty file, empty string. ("X" , "X" ), # No delimiter, full content. ("\n" , "\n"), ("\n\n", "\n"), # UTF16/32 encoded U+270A (b"\n\x00\n'\n\x00"/utf16) (b'\n\xe2\x9c\x8a\n'.decode(), b'\xe2\x9c\x8a\n'.decode()), ) for txt, match in edges: for enc,sep in seps.items(): assert readlast(BytesIO(txt.encode(enc)), sep).decode(enc) == match if __name__ == "__main__": import sys for path in sys.argv[1:]: with open(path) as f: print(f.readline() , end="") print(readlast(f,"\n"), end="")
quelle
tail_n
.File "mapper1.2.2.py", line 17, in get_last_line f.seek(-2, 2) IOError: [Errno 22] Invalid argument
while f.read(1) != "\n":
solltewhile f.read(1) != b"\n":
io.UnsupportedOperation: can't do nonzero end-relative seeks
, müssen Sie dies in zwei Schritten tun:f.seek(size+offset,os.SEEK_SET)
Hier ist eine modifizierte Version der Antwort von SilentGhost, die macht, was Sie wollen.
with open(fname, 'rb') as fh: first = next(fh) offs = -100 while True: fh.seek(offs, 2) lines = fh.readlines() if len(lines)>1: last = lines[-1] break offs *= 2 print first print last
Hier ist keine Obergrenze für die Zeilenlänge erforderlich.
quelle
Können Sie Unix-Befehle verwenden? Ich denke mit
head -1
undtail -n 1
sind wahrscheinlich die effizientesten Methoden. Alternativ können Sie eine einfache verwendenfid.readline()
, um die erste Zeile zu erhaltenfid.readlines()[-1]
, aber das kann zu viel Speicherplatz beanspruchen.quelle
os.popen("tail -n 1 %s" % filename).read()
bekommt die letzte Zeile schön.os.popen("tail -n 1 %s" % filename).read()
-> Veraltet seit Version 2.6Dies ist meine Lösung, die auch mit Python3 kompatibel ist. Es verwaltet auch Grenzfälle, aber es fehlt die Unterstützung von utf-16:
def tail(filepath): """ @author Marco Sulla ([email protected]) @date May 31, 2016 """ try: filepath.is_file fp = str(filepath) except AttributeError: fp = filepath with open(fp, "rb") as f: size = os.stat(fp).st_size start_pos = 0 if size - 1 < 0 else size - 1 if start_pos != 0: f.seek(start_pos) char = f.read(1) if char == b"\n": start_pos -= 1 f.seek(start_pos) if start_pos == 0: f.seek(start_pos) else: char = "" for pos in range(start_pos, -1, -1): f.seek(pos) char = f.read(1) if char == b"\n": break return f.readline()
Es wird durch Trasps Antwort und AnotherParkers Kommentar inspiriert .
quelle
Öffnen Sie zuerst die Datei im Lesemodus. Verwenden Sie dann die readlines () -Methode, um zeilenweise zu lesen. Alle in einer Liste gespeicherten Zeilen. Jetzt können Sie Listenschnitte verwenden, um die ersten und letzten Zeilen der Datei abzurufen.
a=open('file.txt','rb') lines = a.readlines() if lines: first_line = lines[:1] last_line = lines[-1]
quelle
w=open(file.txt, 'r') print ('first line is : ',w.readline()) for line in w: x= line print ('last line is : ',x) w.close()
Die
for
Schleife verläuft durch die Zeilen undx
erhält die letzte Zeile bei der letzten Iteration.quelle
with open("myfile.txt") as f: lines = f.readlines() first_row = lines[0] print first_row last_row = lines[-1] print last_row
quelle
f.readlines()[-1]
Insead der neuen Variablen. 0 = Erste Zeile , 1 = Zweite Zeile , -1 = Letzte Zeile , -2 = Zeile vor der letzten Zeile ...Hier ist eine Erweiterung der Antwort von @ Trasp, die zusätzliche Logik für die Behandlung des Eckfalls einer Datei mit nur einer Zeile enthält. Es kann hilfreich sein, diesen Fall zu behandeln, wenn Sie wiederholt die letzte Zeile einer Datei lesen möchten, die ständig aktualisiert wird. Wenn Sie ohne dies versuchen, die letzte Zeile einer Datei zu erfassen, die gerade erstellt wurde und nur eine Zeile enthält,
IOError: [Errno 22] Invalid argument
wird dies ausgelöst.def tail(filepath): with open(filepath, "rb") as f: first = f.readline() # Read the first line. f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... try: f.seek(-2, 1) # ...jump back the read byte plus one more. except IOError: f.seek(-1, 1) if f.tell() == 0: break last = f.readline() # Read last line. return last
quelle
Niemand erwähnte die Verwendung von umgekehrt:
f=open(file,"r") r=reversed(f.readlines()) last_line_of_file = r.next()
quelle
Die erste Zeile zu bekommen ist trivial einfach. Angenommen , Sie kennen eine ungefähre Obergrenze für die Zeilenlänge, suchen Sie für die letzte Zeile einen gewissen Betrag , indem
SEEK_END
Sie das Ende der vorletzten Zeile finden und dann die letzte Zeile readline () .quelle
with open(filename, "rb") as f:#Needs to be in binary mode for the seek from the end to work first = f.readline() if f.read(1) == '': return first f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... f.seek(-2, 1) # ...jump back the read byte plus one more. last = f.readline() # Read last line. return last
Die obige Antwort ist eine modifizierte Version der obigen Antworten, die den Fall behandelt, dass die Datei nur eine Zeile enthält
quelle