Как читать две строки из файла одновременно с помощью python
я кодирую скрипт python, который анализирует текстовый файл. Формат этого текстового файла таков, что каждый элемент в файле использует две строки и для удобства я хотел бы прочитать обе строки перед разбором. Можно ли это сделать на Python?
Я хотел бы что-нибудь вроде:
f = open(filename, "r")
for line in f:
line1 = line
line2 = f.readline()
f.close
но это ломает говоря, что:
ValueError: смешивание итерации и методов чтения будет потеряно данные
по теме:
- каков самый "питонический" способ перебора списка в кусках?
14 ответов:
аналогичный вопрос здесь. Вы не можете смешивать итерации и readline, поэтому вам нужно использовать один или другой.
while True: line1 = f.readline() line2 = f.readline() if not line2: break # EOF ...
import itertools with open('a') as f: for line1,line2 in itertools.izip_longest(*[f]*2): print(line1,line2)
izip_longest возвращает итератор, поэтому он должен работать хорошо, даже если файл очень большой.
если есть нечетное число строк, то line2 получает значение None на последней итерации.
izip_longest находится в itertools, если у вас есть python 2.6 или более поздней версии. Если вы используете предыдущую версию, вы можете выбрать реализацию python izip_longest здесь. В Python3,
itertools.izip_longest
переименованitertools.zip_longest
.
в комментариях, был задан вопрос, если это решение читает весь файл сначала, а затем повторяет файл во второй раз. Я считаю, что это не так. Элемент
with open('a') as f
строка открывает дескриптор файла, но не читает файл.f
является итератором, поэтому его содержимое не читается до запроса. izip_longest принимает итераторы в качестве аргументов и возвращает итератор.izip_longest действительно подается один и тот же итератор, f, дважды. Но в конечном итоге происходит то, что f. next () (или next (f) в Python3) называется по первому аргументу, а затем по второму аргументу. Поскольку next () вызывается на одном и том же базовом итераторе, получаются последовательные строки. Это очень отличается от чтения во всем файле. Действительно, цель использования итераторов заключается в том, чтобы избежать чтения всего файла.
поэтому я считаю, что решение работает по желанию - файл читается только один раз с помощью for-loop.
чтобы подтвердить это, я запустил решение izip_longest против решения используя
f.readlines()
. Я поставил raw_input() в конце, чтобы приостановить скрипты, и побежалps axuw
на каждого:% ps axuw | grep izip_longest_method.py
unutbu 11119 2.2 0.2
4520 2712pts/0 S+ 21:14 0:00 python /home/unutbu/pybin/izip_longest_method.py bigfile
% ps axuw | grep readlines_method.py
unutbu 11317 6.5 8.8
93908 91680pts/0 S+ 21:16 0:00 python /home/unutbu/pybin/readlines_method.py bigfile
The
readlines
четко читает весь файл сразу. Так какizip_longest_method
использует гораздо меньше памяти, я думаю, что можно с уверенностью заключить, что он не читает весь файл сразу.
использовать линии.далее (), например
f=open("file") for line in f: print line nextline=f.next() print "next line", nextline .... f.close()
Я бы действовал так же, как ghostdog74, только с попыткой снаружи и несколько модификаций:
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
это сохраняет код простым и в то же время надежным. Используя
with
закрывает файл, если что-то еще происходит, или просто закрывает ресурсы, как только вы исчерпали его и выходите из цикла.отметим, что
with
потребности 2.6 или 2.5 сwith_statement
функция включена.
работает для четных и нечетных файлов. Он просто игнорирует непревзойденную последнюю строку.
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()
Если у вас есть большие файлы, это не правильный подход. Вы загружаете весь файл в память с помощью readlines (). Однажды я написал класс, который читал файл, сохраняя позицию fseek каждого начала строки. Это позволяет получить определенные строки, не имея весь файл в памяти, а также вы можете идти вперед и назад.
я вставляю его сюда. Лицензия является общественным достоянием, то есть, делай с ним что хочешь. Обратите внимание, что этот класс был написан 6 лет назад, и с тех пор я его не трогал и не проверял. Я думаю, что это даже не соответствует файлу. предостережение emptor. Также обратите внимание, что это перебор для вашей проблемы. Я не утверждаю, что вы обязательно должны пойти этим путем, но у меня был этот код, и мне нравится делиться им, если вам нужен более сложный доступ.
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
Как насчет этого, кто-нибудь видит проблемы с этим
f=open('file_name') for line,line2 in zip(f,f): print line,line2
file_name = 'your_file_name' file_open = open(file_name, 'r') def handler(line_one, line_two): print(line_one, line_two) while file_open: try: one = file_open.next() two = file_open.next() handler(one, two) except(StopIteration): file_open.close() break
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
f = open(filename, "r") for line in f: line1 = line f.next() f.close
прямо сейчас, вы можете прочитать файл каждые две строки. Если вам нравится, вы также можете проверить статус f перед
f.next()
моя идея состоит в том, чтобы создать генератор, который читает две строчки из файла, и возвращает это как 2-ки, это означает, тогда вы можете перебрать результаты.
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
Если у вас есть нечетное количество линий, он не будет работать идеально, но это должно дать вам хороший набросок.
У меня в прошлом месяце. Я попробовал цикл while с f. readline (), а также f.readlines (). Мой файл данных не огромен, поэтому я, наконец, выбрал f. readlines (), что дает мне больше контроля над индексом, в противном случае Я должен использовать f. seek () для перемещения вперед и назад указателя файла.
мой случай сложнее, чем ОП. Потому что мой файл данных является более гибким на сколько линий должно быть разобран каждый раз, так Я должен проверить несколько условий, прежде чем я могу разобрать данные.
еще одна проблема, которую я узнал о f.seek (), заключается в том, что он не очень хорошо обрабатывает utf-8, когда я использую кодеки.open (", 'r', 'utf-8'), (не совсем уверен в виновнике, в конце концов я отказался от этого подхода.)
простой маленький читатель. Он будет тянуть линии в парах из двух и возвращать их в виде кортежа, как вы повторяете над объектом. Вы можете закрыть его вручную или он закроется сам, когда он выпадает из области видимости.
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
Если файл имеет разумный размер, другой подход, который использует список-понимание чтобы прочитать весь файл в список 2-кортежей это:
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))