【问题标题】:Is there an easy way to tell which line number a file pointer is on?有没有一种简单的方法来判断文件指针在哪个行号上?
【发布时间】:2011-09-16 01:26:08
【问题描述】:

在 Python 2.5 中,我正在使用文件指针读取结构化文本数据文件(大小约为 30 MB):

fp = open('myfile.txt', 'r')
line = fp.readline()
# ... many other fp.readline() processing steps, which
# are used in different contexts to read the structures

但是,在解析文件时,我遇到了一些有趣的事情,我想报告其行号,因此我可以在文本编辑器中调查该文件。我可以使用fp.tell() 告诉我字节偏移在哪里(例如16548974L),但是没有“fp.tell_line_number()”可以帮助我将其转换为行号。

是否有 Python 内置或扩展可以轻松跟踪和“判断”文本文件指针所在的行号?

注意:我是 not asking 来使用 line_number += 1 样式的计数器,因为我在不同的上下文中调用 fp.readline(),这种方法需要更多的调试,而不是在右上角插入计数器代码。

【问题讨论】:

    标签: python text-files line-numbers


    【解决方案1】:

    使用with 上下文管理器打开文件并枚举for 循环中的行。

    with open('file_name.ext', 'r') as f:
        [(line_num, line) for line_num, line in enumerate(f)]
    

    【讨论】:

    • 您能否详细说明您的答案?
    • 实际上我做到了,但不知道为什么它错过了。再次添加,谢谢提醒。
    【解决方案2】:

    最近遇到了一个类似的问题,想出了这个基于类的解决方案。

    class TextFileProcessor(object):
    
        def __init__(self, path_to_file):
            self.print_line_mod_number = 0
            self.__path_to_file = path_to_file
            self.__line_number = 0
    
        def __printLineNumberMod(self):
            if self.print_line_mod_number != 0:
                if self.__line_number % self.print_line_mod_number == 0:
                    print(self.__line_number)
    
        def processFile(self):
            with open(self.__path_to_file, 'r', encoding='utf-8') as text_file:
                for self.__line_number, line in enumerate(text_file, start=1):
                    self.__printLineNumberMod()
    
                    # do some stuff with line here.
    

    print_line_mod_number 属性设置为您要记录的节奏,然后调用processFile

    例如...如果您想要每 100 行反馈一次,它看起来像这样。

    tfp = TextFileProcessor('C:\\myfile.txt')
    tfp.print_line_mod_number = 100
    tfp.processFile()
    

    控制台输出为

    100
    200
    300
    400
    etc...
    

    【讨论】:

      【解决方案3】:

      以下代码将在遍历文件时打印行号(指针当前所在的位置)('testfile')

      file=open("testfile", "r")
      for line_no, line in enumerate(file):
          print line_no     # The content of the line is in variable 'line'
      file.close()
      

      输出:

      1
      2
      3
      ...
      

      【讨论】:

      • 感谢您的建议。我会
      【解决方案4】:

      此问题的典型解决方案是定义一个新类,该类包装一个现有的 file 实例,它会自动计算数字。像这样的东西(只是在我的脑海中,我还没有测试过):

      class FileLineWrapper(object):
          def __init__(self, f):
              self.f = f
              self.line = 0
          def close(self):
              return self.f.close()
          def readline(self):
              self.line += 1
              return self.f.readline()
          # to allow using in 'with' statements 
          def __enter__(self):
              return self
          def __exit__(self, exc_type, exc_val, exc_tb):
              self.close()
      

      像这样使用它:

      f = FileLineWrapper(open("myfile.txt", "r"))
      f.readline()
      print(f.line)
      

      看起来标准模块fileinput 做了很多相同的事情(以及其他一些事情);如果你愿意,你可以改用它。

      【讨论】:

      • +1,很好的简单解决方案,因为它只需要更改 open 调用。您可能希望为也使用的任何其他函数(例如close)提供包装器,但它们应该是相当小的传递函数。
      • 内置的fileinput 似乎可以无缝工作:fp = fileinput.input("myfile.txt"); fp.readline(); fp.lineno()
      • 还有__iter__ = lambda self: iter(self.f)
      • 这样我们就可以写:for line in f:
      【解决方案5】:

      您可能会发现fileinput 模块很有用。它提供了一个通用接口,用于迭代任意数量的文件。文档中的一些相关亮点:

      fileinput.lineno()

      返回刚刚读取的行的累积行号。在读取第一行之前,返回 0。在读取最后一个文件的最后一行之后,返回该行的行号。

      fileinput.filelineno()

      返回当前文件的行号。在读取第一行之前,返回 0。在读取最后一个文件的最后一行之后,返回文件中该行的行号。

      【讨论】:

      • 小警告:fileinput 似乎不支持 Python2.7 中的 with 语句...
      【解决方案6】:

      关于solution by @eyquem,我建议将mode='r' 与fileinput 模块和fileinput.lineno() 选项一起使用,它对我有用。

      这是我在代码中实现这些选项的方式。

          table=fileinput.input('largefile.txt',mode="r")
          if fileinput.lineno() >= stop : # you can disregard the IF condition but I am posting to illustrate the approach from my code.
                 temp_out.close()
      

      【讨论】:

      • 这没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方发表评论 - 您可以随时评论自己的帖子,一旦您有足够的reputation,您就可以comment on any post。 - From Review
      • @Prune - 感谢您的评论,我已经包含了我学习中的代码 sn-p,以使我的建议更加清晰。
      • 请注意,“以上”在答案中没有上下文。答案投票会发生变化,并且可以以多种不同的方式对答案进行排序。最好链接到您所指的答案。
      【解决方案7】:

      下面的代码创建了一个函数Which_Line_for_Position(pos),它给出位置pos行号,也就是说行数 位于文件中位置 pos 的字符所在的位置。

      这个函数可以使用任何位置作为参数,独立于文件指针当前位置的值和函数调用之前这个指针移动的历史。

      因此,使用此函数,人们不仅限于在行的不间断迭代期间确定当前行的编号,就像 Greg Hewgill 的解决方案一样。

      with open(filepath,'rb') as f:
          GIVE_NO_FOR_END = {}
          end = 0
          for i,line in enumerate(f):
              end += len(line)
              GIVE_NO_FOR_END[end] = i
          if line[-1]=='\n':
              GIVE_NO_FOR_END[end+1] = i+1
          end_positions = GIVE_NO_FOR_END.keys()
          end_positions.sort()
      
      def Which_Line_for_Position(pos,
                                  dic = GIVE_NO_FOR_END,
                                  keys = end_positions,
                                  kmax = end_positions[-1]):
          return dic[(k for k in keys if pos < k).next()] if pos<kmax else None
      

      .

      借助 fileinput 模块可以编写相同的解决方案:

      import fileinput
      
      GIVE_NO_FOR_END = {}
      end = 0
      for line in fileinput.input(filepath,'rb'):
          end += len(line)
          GIVE_NO_FOR_END[end] = fileinput.filelineno()
      if line[-1]=='\n':
          GIVE_NO_FOR_END[end+1] = fileinput.filelineno()+1
      fileinput.close()
      
      end_positions = GIVE_NO_FOR_END.keys()
      end_positions.sort()
      
      def Which_Line_for_Position(pos,
                                  dic = GIVE_NO_FOR_END,
                                  keys = end_positions,
                                  kmax = end_positions[-1]):
          return dic[(k for k in keys if pos < k).next()] if pos<kmax else None
      

      但是这个解决方案有一些不便之处:

      • 需要导入模块fileinput
      • 它会删除文件的所有内容!我的代码中一定有问题,但我对 fileinput 的了解不足以找到它。还是 fileinput.input() 函数的正常行为?
      • 似乎在启动任何迭代之前首先完全读取文件。如果是这样,对于一个非常大的文件,文件的大小可能会超过 RAM 的容量。我不确定这一点:我尝试使用 1,5 GB 的文件进行测试,但它相当长,我暂时放弃了这一点。如果这一点是正确的,则构成使用 enumerate() 的其他解决方案的论据

      .

      示例:

      text = '''Harold Acton (1904–1994)
      Gilbert Adair (born 1944)
      Helen Adam (1909–1993)
      Arthur Henry Adams (1872–1936)
      Robert Adamson (1852–1902)
      Fleur Adcock (born 1934)
      Joseph Addison (1672–1719)
      Mark Akenside (1721–1770)
      James Alexander Allan (1889–1956)
      Leslie Holdsworthy Allen (1879–1964)
      William Allingham (1824/28-1889)
      Kingsley Amis (1922–1995)
      Ethel Anderson (1883–1958)
      Bruce Andrews (born 1948)
      Maya Angelou (born 1928)
      Rae Armantrout (born 1947)
      Simon Armitage (born 1963)
      Matthew Arnold (1822–1888)
      John Ashbery (born 1927)
      Thomas Ashe (1836–1889)
      Thea Astley (1925–2004)
      Edwin Atherstone (1788–1872)'''
      
      
      #with open('alao.txt','rb') as f:
      
      f = text.splitlines(True)
      # argument True in splitlines() makes the newlines kept
      
      GIVE_NO_FOR_END = {}
      end = 0
      for i,line in enumerate(f):
          end += len(line)
          GIVE_NO_FOR_END[end] = i
      if line[-1]=='\n':
          GIVE_NO_FOR_END[end+1] = i+1
      end_positions = GIVE_NO_FOR_END.keys()
      end_positions.sort()
      
      
      print '\n'.join('line %-3s  ending at position %s' % (str(GIVE_NO_FOR_END[end]),str(end))
                      for end in end_positions)
      
      def Which_Line_for_Position(pos,
                                  dic = GIVE_NO_FOR_END,
                                  keys = end_positions,
                                  kmax = end_positions[-1]):
          return dic[(k for k in keys if pos < k).next()] if pos<kmax else None
      
      print
      for x in (2,450,320,104,105,599,600):
          print 'pos=%-6s   line %s' % (x,Which_Line_for_Position(x))
      

      结果

      line 0    ending at position 25
      line 1    ending at position 51
      line 2    ending at position 74
      line 3    ending at position 105
      line 4    ending at position 132
      line 5    ending at position 157
      line 6    ending at position 184
      line 7    ending at position 210
      line 8    ending at position 244
      line 9    ending at position 281
      line 10   ending at position 314
      line 11   ending at position 340
      line 12   ending at position 367
      line 13   ending at position 393
      line 14   ending at position 418
      line 15   ending at position 445
      line 16   ending at position 472
      line 17   ending at position 499
      line 18   ending at position 524
      line 19   ending at position 548
      line 20   ending at position 572
      line 21   ending at position 600
      
      pos=2        line 0
      pos=450      line 16
      pos=320      line 11
      pos=104      line 3
      pos=105      line 4
      pos=599      line 21
      pos=600      line None
      

      .

      然后,有了函数 Which_Line_for_Position() ,很容易获得当前行的编号:只需将 f.tell() 作为参数传递给函数

      警告:当使用f.tell()并在文件中移动文件指针时,绝对有必要以二进制模式打开文件: 'rb''rb+''ab' 或 ....

      【讨论】:

        【解决方案8】:

        我不这么认为,不是您想要的方式(就像open 返回的 Python 文件句柄的标准内置功能一样)。

        如果您不适合在阅读行时手动跟踪行号或使用包装类(顺便说一下,GregH 和 senderle 的出色建议),那么我认为您必须简单地使用fp.tell() 数字,然后回到文件的开头,一直阅读直到到达那里。

        这不是一个糟糕的选择,因为我假设错误条件比一切正常工作的可能性要小。如果一切正常,则没有影响。

        如果有错误,那么您需要重新扫描文件。如果文件很大,可能会影响您的感知性能 - 如果这是一个问题,您应该考虑到这一点。

        【讨论】:

          【解决方案9】:

          一种方法可能是遍历该行并明确计算已经看到的行数:

          >>> f=open('text.txt','r')
          >>> from itertools import izip
          >>> from itertools import count
          >>> f=open('test.java','r')
          >>> for line_no,line in izip(count(),f):
          ...     print line_no,line
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-06-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多