Estoy codificando una secuencia de comandos de Python que analiza un archivo de texto. El formato de este archivo de texto es tal que cada elemento del archivo usa dos líneas y, por conveniencia, me gustaría leer ambas líneas antes de analizar. ¿Se puede hacer esto en Python?
Me gustaría algo como:
f = open(filename, "r")
for line in f:
line1 = line
line2 = f.readline()
f.close
Pero esto rompe diciendo que:
ValueError: la mezcla de iteración y métodos de lectura perdería datos
next(f)
lugar.Respuestas:
Pregunta similar aquí . No puede mezclar iteración y readline, por lo que debe usar uno u otro.
while True: line1 = f.readline() line2 = f.readline() if not line2: break # EOF ...
fuente
import itertools with open('a') as f: for line1,line2 in itertools.zip_longest(*[f]*2): print(line1,line2)
itertools.zip_longest()
devuelve un iterador, por lo que funcionará bien incluso si el archivo tiene miles de millones de líneas.Si hay un número impar de líneas,
line2
se establece enNone
en la última iteración.En Python2 necesitas usar
izip_longest
en su lugar.En los comentarios, se preguntó si esta solución lee todo el archivo primero y luego itera sobre el archivo por segunda vez. Creo que no es así. La
with open('a') as f
línea abre un identificador de archivo, pero no lee el archivo.f
es un iterador, por lo que su contenido no se lee hasta que se solicita.zip_longest
toma iteradores como argumentos y devuelve un iterador.zip_longest
de hecho, se alimenta con el mismo iterador, f, dos veces. Pero lo que termina sucediendo es quenext(f)
se invoca en el primer argumento y luego en el segundo argumento. Dado quenext()
se llama en el mismo iterador subyacente, se generan líneas sucesivas. Esto es muy diferente a leer todo el archivo. De hecho, el propósito de usar iteradores es precisamente evitar leer todo el archivo.Por lo tanto, creo que la solución funciona como se desea: el bucle for solo lee el archivo una vez.
Para corroborar esto, ejecuté la solución zip_longest frente a una solución que usa
f.readlines()
. Puse uninput()
al final para pausar los scripts y ejecutéps axuw
cada uno:unutbu 11119 2.2 0.2
4520 2712pts/0 S+ 21:14 0:00 python /home/unutbu/pybin/zip_longest_method.py bigfile
unutbu 11317 6.5 8.8
93908 91680pts/0 S+ 21:16 0:00 python /home/unutbu/pybin/readlines_method.py bigfile
Se
readlines
lee claramente en todo el archivo a la vez. Dado quezip_longest_method
utiliza mucha menos memoria, creo que es seguro concluir que no está leyendo todo el archivo a la vez.fuente
(*[f]*2)
porque muestra que puede obtener fragmentos de cualquier tamaño que desee simplemente cambiando el número (por lo que no editaré la respuesta para cambiarla), pero en este caso(f, f)
probablemente sea más fácil de escribir.lines
lugar deline1, line2
entonces solo necesita cambiar un número (2
) para leern
líneas a la vez.utilizar
next()
, por ejemplowith open("file") as f: for line in f: print(line) nextline = next(f) print("next line", nextline) ....
fuente
StopIteration
aumento.nextline = next(f,None)
Procedería de manera similar a ghostdog74 , solo que con la prueba externa y algunas modificaciones:
try: with open(filename) as f: for line1 in f: line2 = f.next() # process line1 and line2 here except StopIteration: print "(End)" # do whatever you need to do with line1 alone
Esto mantiene el código simple pero robusto. Utilizando el
with
cierra el archivo si sucede algo más, o simplemente cierra los recursos una vez que lo haya agotado y salga del ciclo.Tenga en cuenta que
with
necesita 2.6 o 2.5 con lawith_statement
función habilitada.fuente
¿Qué tal este, si alguien ve un problema con él?
with open('file_name') as f: for line1, line2 in zip(f, f): print(line1, line2)
fuente
for l1, l2, l3 in zip(f, f, f):
y así sucesivamente; de nuevo, las últimas 1 o 2 líneas se descartarán si el número de líneas no es divisible por 3.Funciona para archivos pares e impares. Simplemente ignora la última línea incomparable.
f=file("file") lines = f.readlines() for even, odd in zip(lines[0::2], lines[1::2]): print "even : ", even print "odd : ", odd print "end cycle" f.close()
Si tiene archivos grandes, este no es el enfoque correcto. Está cargando todo el archivo en la memoria con readlines (). Una vez escribí una clase que leyó el archivo guardando la posición fseek de cada inicio de línea. Esto le permite obtener líneas específicas sin tener todo el archivo en la memoria, y también puede avanzar y retroceder.
Lo pego aquí. La licencia es de dominio público, es decir, haz lo que quieras con ella. Tenga en cuenta que esta clase se escribió hace 6 años y no la he tocado ni revisado desde entonces. Creo que ni siquiera es compatible con archivos. Caveat emptor . Además, tenga en cuenta que esto es excesivo para su problema. No estoy diciendo que definitivamente deba ir por este camino, pero tenía este código y disfruto compartirlo si necesita un acceso más complejo.
import string import re class FileReader: """ Similar to file class, but allows to access smoothly the lines as when using readlines(), with no memory payload, going back and forth, finding regexps and so on. """ def __init__(self,filename): # fold>> self.__file=file(filename,"r") self.__currentPos=-1 # get file length self.__file.seek(0,0) counter=0 line=self.__file.readline() while line != '': counter = counter + 1 line=self.__file.readline() self.__length = counter # collect an index of filedescriptor positions against # the line number, to enhance search self.__file.seek(0,0) self.__lineToFseek = [] while True: cur=self.__file.tell() line=self.__file.readline() # if it's not null the cur is valid for # identifying a line, so store self.__lineToFseek.append(cur) if line == '': break # <<fold def __len__(self): # fold>> """ member function for the operator len() returns the file length FIXME: better get it once when opening file """ return self.__length # <<fold def __getitem__(self,key): # fold>> """ gives the "key" line. The syntax is import FileReader f=FileReader.FileReader("a_file") line=f[2] to get the second line from the file. The internal pointer is set to the key line """ mylen = self.__len__() if key < 0: self.__currentPos = -1 return '' elif key > mylen: self.__currentPos = mylen return '' self.__file.seek(self.__lineToFseek[key],0) counter=0 line = self.__file.readline() self.__currentPos = key return line # <<fold def next(self): # fold>> if self.isAtEOF(): raise StopIteration return self.readline() # <<fold def __iter__(self): # fold>> return self # <<fold def readline(self): # fold>> """ read a line forward from the current cursor position. returns the line or an empty string when at EOF """ return self.__getitem__(self.__currentPos+1) # <<fold def readbackline(self): # fold>> """ read a line backward from the current cursor position. returns the line or an empty string when at Beginning of file. """ return self.__getitem__(self.__currentPos-1) # <<fold def currentLine(self): # fold>> """ gives the line at the current cursor position """ return self.__getitem__(self.__currentPos) # <<fold def currentPos(self): # fold>> """ return the current position (line) in the file or -1 if the cursor is at the beginning of the file or len(self) if it's at the end of file """ return self.__currentPos # <<fold def toBOF(self): # fold>> """ go to beginning of file """ self.__getitem__(-1) # <<fold def toEOF(self): # fold>> """ go to end of file """ self.__getitem__(self.__len__()) # <<fold def toPos(self,key): # fold>> """ go to the specified line """ self.__getitem__(key) # <<fold def isAtEOF(self): # fold>> return self.__currentPos == self.__len__() # <<fold def isAtBOF(self): # fold>> return self.__currentPos == -1 # <<fold def isAtPos(self,key): # fold>> return self.__currentPos == key # <<fold def findString(self, thestring, count=1, backward=0): # fold>> """ find the count occurrence of the string str in the file and return the line catched. The internal cursor is placed at the same line. backward is the searching flow. For example, to search for the first occurrence of "hello starting from the beginning of the file do: import FileReader f=FileReader.FileReader("a_file") f.toBOF() f.findString("hello",1,0) To search the second occurrence string from the end of the file in backward movement do: f.toEOF() f.findString("hello",2,1) to search the first occurrence from a given (or current) position say line 150, going forward in the file f.toPos(150) f.findString("hello",1,0) return the string where the occurrence is found, or an empty string if nothing is found. The internal counter is placed at the corresponding line number, if the string was found. In other case, it's set at BOF if the search was backward, and at EOF if the search was forward. NB: the current line is never evaluated. This is a feature, since we can so traverse occurrences with a line=f.findString("hello") while line == '': line.findString("hello") instead of playing with a readline every time to skip the current line. """ internalcounter=1 if count < 1: count = 1 while 1: if backward == 0: line=self.readline() else: line=self.readbackline() if line == '': return '' if string.find(line,thestring) != -1 : if count == internalcounter: return line else: internalcounter = internalcounter + 1 # <<fold def findRegexp(self, theregexp, count=1, backward=0): # fold>> """ find the count occurrence of the regexp in the file and return the line catched. The internal cursor is placed at the same line. backward is the searching flow. You need to pass a regexp string as theregexp. returns a tuple. The fist element is the matched line. The subsequent elements contains the matched groups, if any. If no match returns None """ rx=re.compile(theregexp) internalcounter=1 if count < 1: count = 1 while 1: if backward == 0: line=self.readline() else: line=self.readbackline() if line == '': return None m=rx.search(line) if m != None : if count == internalcounter: return (line,)+m.groups() else: internalcounter = internalcounter + 1 # <<fold def skipLines(self,key): # fold>> """ skip a given number of lines. Key can be negative to skip backward. Return the last line read. Please note that skipLines(1) is equivalent to readline() skipLines(-1) is equivalent to readbackline() and skipLines(0) is equivalent to currentLine() """ return self.__getitem__(self.__currentPos+key) # <<fold def occurrences(self,thestring,backward=0): # fold>> """ count how many occurrences of str are found from the current position (current line excluded... see skipLines()) to the begin (or end) of file. returns a list of positions where each occurrence is found, in the same order found reading the file. Leaves unaltered the cursor position. """ curpos=self.currentPos() list = [] line = self.findString(thestring,1,backward) while line != '': list.append(self.currentPos()) line = self.findString(thestring,1,backward) self.toPos(curpos) return list # <<fold def close(self): # fold>> self.__file.close() # <<fold
fuente
readlines()
llamada también llevará todo a la memoria.fuente
while file_open:
es engañoso debido a que es equivalente awhile True:
en este caso.def readnumlines(file, num=2): f = iter(file) while True: lines = [None] * num for i in range(num): try: lines[i] = f.next() except StopIteration: # EOF or not enough lines available return yield lines # use like this f = open("thefile.txt", "r") for line1, line2 in readnumlines(f): # do something with line1 and line2 # or for line1, line2, line3, ..., lineN in readnumlines(f, N): # do something with N lines
fuente
Mi idea es crear un generador que lea dos líneas del archivo a la vez y lo devuelva como 2 tuplas. Esto significa que luego puede iterar sobre los resultados.
from cStringIO import StringIO def read_2_lines(src): while True: line1 = src.readline() if not line1: break line2 = src.readline() if not line2: break yield (line1, line2) data = StringIO("line1\nline2\nline3\nline4\n") for read in read_2_lines(data): print read
Si tiene un número impar de líneas, no funcionará perfectamente, pero esto debería darle un buen esquema.
fuente
Trabajé en un problema similar el mes pasado. Probé un bucle while con f.readline () y f.readlines (). Mi archivo de datos no es enorme, así que finalmente elegí f.readlines (), lo que me da más control del índice, de lo contrario tengo que usar f.seek () para mover hacia adelante y hacia atrás el puntero del archivo.
Mi caso es más complicado que OP. Debido a que mi archivo de datos es más flexible en cuanto a cuántas líneas se analizarán cada vez, tengo que verificar algunas condiciones antes de poder analizar los datos.
Otro problema que descubrí sobre f.seek () es que no maneja muy bien utf-8 cuando uso codecs.open ('', 'r', 'utf-8'), (no estoy exactamente seguro de la culpable, finalmente renuncié a este enfoque).
fuente
Pequeño lector sencillo. Extraerá líneas en pares de dos y las devolverá como una tupla a medida que itera sobre el objeto. Puede cerrarlo manualmente o se cerrará solo cuando quede fuera de alcance.
class doublereader: def __init__(self,filename): self.f = open(filename, 'r') def __iter__(self): return self def next(self): return self.f.next(), self.f.next() def close(self): if not self.f.closed: self.f.close() def __del__(self): self.close() #example usage one r = doublereader(r"C:\file.txt") for a, h in r: print "x:%s\ny:%s" % (a,h) r.close() #example usage two for x,y in doublereader(r"C:\file.txt"): print "x:%s\ny:%s" % (x,y) #closes itself as soon as the loop goes out of scope
fuente
f = open(filename, "r") for line in f: line1 = line f.next() f.close
Ahora mismo, puede leer el archivo cada dos líneas. Si lo desea, también puede verificar el estado f antes
f.next()
fuente
Si el archivo tiene un tamaño razonable, otro enfoque que usa la comprensión de listas para leer el archivo completo en una lista de 2 tuplas es este:
filaname = '/path/to/file/name' with open(filename, 'r') as f: list_of_2tuples = [ (line,f.readline()) for line in f ] for (line1,line2) in list_of_2tuples: # Work with them in pairs. print('%s :: %s', (line1,line2))
fuente
Este código de Python imprimirá las dos primeras líneas:
import linecache filename = "ooxx.txt" print(linecache.getline(filename,2))
fuente