【问题标题】:How do I read two lines from a file at a time using python如何使用python一次从文件中读取两行
【发布时间】:2016-05-09 10:15:44
【问题描述】:

我正在编写一个解析文本文件的 python 脚本。这个文本文件的格式是这样的,文件中的每个元素都使用两行,为了方便起见,我想在解析之前阅读这两行。这可以在 Python 中完成吗?

我想要一些类似的东西:

f = open(filename, "r")
for line in f:
    line1 = line
    line2 = f.readline()

f.close

但这打破了说:

ValueError:混合迭代和读取方法会丢失数据

相关:

【问题讨论】:

  • 将 f.readline() 更改为 f.next(),一切就绪。
  • @Paul 这 f.next() 仍然有效吗?我收到此错误 AttributeError: '_io.TextIOWrapper' object has no attribute 'next'
  • @SKR 在 Python 3 上你必须改用 next(f)

标签: python


【解决方案1】:

类似问题here。您不能混合使用迭代和 readline,因此您需要使用其中之一。

while True:
    line1 = f.readline()
    line2 = f.readline()
    if not line2: break  # EOF
    ...

【讨论】:

    【解决方案2】:
    import itertools
    with open('a') as f:
        for line1,line2 in itertools.zip_longest(*[f]*2):
            print(line1,line2)
    

    itertools.zip_longest() 返回一个迭代器,因此即使文件长达数十亿行,它也能正常工作。

    如果有奇数行,则在最后一次迭代中将line2 设置为None

    在 Python2 上,您需要改用 izip_longest


    在 cmets 中,已询问此解决方案是否首先读取整个文件,然后再次遍历该文件。 我相信它没有。 with open('a') as f 行打开文件句柄,但不读取文件。 f 是一个迭代器,因此在请求之前不会读取其内容。 zip_longest 将迭代器作为参数,并返回一个迭代器。

    zip_longest 确实被提供了两次相同的迭代器 f。但最终发生的是next(f) 在第一个参数上调用,然后在第二个参数上调用。由于next() 在同一个底层迭代器上被调用,因此产生了连续的行。这与读取整个文件非常不同。事实上,使用迭代器的目的正是为了避免读取整个文件。

    因此,我相信该解决方案可以按预期工作——该文件仅由 for 循环读取一次。

    为了证实这一点,我运行了 zip_longest 解决方案与使用 f.readlines() 的解决方案。我在末尾放了一个input() 来暂停脚本,并在每个脚本上运行ps axuw

    % ps axuw | grep zip_longest_method.py
    

    unutbu 11119 2.2 0.2 4520 2712 pts/0 S+ 21:14 0:00 python /home/unutbu/pybin/zip_longest_method.py bigfile

    % ps axuw | grep readlines_method.py
    

    unutbu 11317 6.5 8.8 93908 91680 pts/0 S+ 21:16 0:00 python /home/unutbu/pybin/readlines_method.py bigfile

    readlines 清楚地一次读取整个文件。由于zip_longest_method 使用的内存要少得多,我认为可以断定它不是一次读取整个文件。

    【讨论】:

    • 我喜欢(*[f]*2),因为它表明您只需更改数字即可获得所需的任何大小的块(因此我不会编辑答案来更改它),但在这种情况下@ 987654346@ 可能更容易输入。
    • 如果您使用 lines 而不是 line1, line2,那么您只需更改一个数字 (2) 即可一次读取 n 行。
    【解决方案3】:

    使用next(),例如

    with open("file") as f:
        for line in f:
            print(line)
            nextline = next(f)
            print("next line", nextline)
            ....
    

    【讨论】:

    • 正如 RedGlyph 在他的这个答案版本中指出的那样,奇数行将导致 StopIteration 被提升。
    • next( ) 现在支持默认参数以避免异常:nextline = next(f,None)
    【解决方案4】:

    我会以与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,或启用 with_statement 功能的 2.5。

    【讨论】:

      【解决方案5】:

      这个怎么样,有人发现它有问题

      with open('file_name') as f:
          for line1, line2 in zip(f, f):
              print(line1, line2)
      

      【讨论】:

      • 如果您的文件有奇数行,这将丢弃最后一行。好消息是您可以使用for l1, l2, l3 in zip(f, f, f): 等将其扩展为一次读取 3 行;同样,如果行数不能被 3 整除,则最后 1 或 2 行将被丢弃。
      【解决方案6】:

      适用于偶数和奇数长度的文件。它只是忽略了不匹配的最后一行。

      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 年前写的,从那以后我就没有碰过或检查过它。我认为它甚至不符合文件要求。 告诫购买者。另外,请注意,这对您的问题来说太过分了。我并不是说你绝对应该这样做,但我有这段代码,如果你需要更复杂的访问,我很乐意分享它。

      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
      

      【讨论】:

      • 你可能想要使用 itertools.izip() 代替,特别是对于大文件!
      • 即使使用 izip,像这样对列表进行切片也会将所有内容拉入内存。
      • 实际上readlines() 调用也会将所有内容都拉入内存。
      • 我不喜欢你的课。您在初始化文件时对整个文件进行了两次迭代。对于短行的大文件,节省的内存并不多。
      • @Steve:是的,可悲的是。但是 zip 会通过创建整个元组列表(除非它是 Python 3)在内存中添加一个额外的层,其中 izip 将一次生成一个元组。我认为这就是你的意思,但我还是想澄清我之前的评论:-)
      【解决方案7】:
      文件名 = '你的文件名' file_open = open(file_name, 'r') def 处理程序(line_one,line_two): 打印(line_one,line_two) 而文件打开: 尝试: 一 = file_open.next() 二 = file_open.next() 处理程序(一,二) 除了(停止迭代): file_open.close() 休息

      【讨论】:

      • while file_open: 具有误导性,因为在这种情况下它等同于 while True:
      • 这是故意的,尽管我同意做'while True'表示你需要休息一下才能摆脱循环。我选择不这样做是因为我相信(再次有争议)它以这种方式读起来更好,毫无疑问文件需要保持打开多长时间以及同时如何处理它。不过,大多数时候我也会为自己做'while True'。
      【解决方案8】:
      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
      

      【讨论】:

        【解决方案9】:

        我的想法是创建一个生成器,它一次从文件中读取两行,并将其作为 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
        

        如果你有奇数行,它不会完美地工作,但这应该会给你一个很好的轮廓。

        【讨论】:

          【解决方案10】:

          上个月我曾研究过一个类似的问题。我用 f.readline() 和 f.readlines() 尝试了一个 while 循环。 我的数据文件并不大,所以最后选择了f.readlines(),它让我对索引有更多的控制权,否则 我必须使用 f.seek() 来回移动文件指针。

          我的情况比 OP 更复杂。因为我的数据文件每次解析多少行比较灵活,所以 在解析数据之前,我必须检查一些条件。

          我发现 f.seek() 的另一个问题是,当我使用 codecs.open('', 'r', 'utf-8') 时,它不能很好地处理 utf-8,(不完全是确定罪魁祸首,最终我放弃了这种方法。)

          【讨论】:

            【解决方案11】:

            简单的小读者。当您遍历对象时,它将成对地拉出两条线并将它们作为元组返回。您可以手动关闭它,否则它会在超出范围时自行关闭。

            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
            

            【讨论】:

              【解决方案12】:
              f = open(filename, "r")
              for line in f:
                  line1 = line
                  f.next()
              
              f.close
              

              现在,您可以每两行读取一次文件。如果你喜欢你也可以查看f.next()之前的f状态

              【讨论】:

                【解决方案13】:

                如果文件大小合理,另一种使用 list-comprehension 将整个文件读入 2 元组列表 的方法是: p>

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

                【讨论】:

                  【解决方案14】:

                  此 Python 代码将打印前两行:

                  import linecache  
                  filename = "ooxx.txt"  
                  print(linecache.getline(filename,2))
                  

                  【讨论】:

                    猜你喜欢
                    • 2012-12-31
                    • 1970-01-01
                    • 2020-10-24
                    • 2017-06-15
                    • 2015-09-22
                    • 2021-12-28
                    • 2013-10-14
                    • 2023-04-10
                    相关资源
                    最近更新 更多