2011-06-16 30 views
6

在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()和方法需要更多的调试比它的价值插入计数器代码的右下角。

回答

13

这个问题的一个典型解决方案是定义一个新类,它包装了一个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确实大同小异东西(以及其他一些东西);如果你喜欢,你可以使用它。

+0

+1,好简单的解决方案,因为它不仅需要'open'呼吁改变。您可能想为其他任何使用的函数提供包装(例如'close'),但它们应该是相当小的pass-thru函数。 – paxdiablo 2011-06-16 04:32:49

+0

哦,对,'close'很方便,我会补充一点。 – 2011-06-16 04:34:30

+0

这两种解决方案都很棒,太棒了! – 2011-06-16 04:36:50

1

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

如果您不习惯于在阅读线条或使用包装类时手动跟踪行号(顺便说一下,GregH和senderle提供的出色建议),那么我认为您只需简单地使用fp.tell()图,并返回到文件的开头,阅读,直到你到达那里。

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

如果出现错误,那么您有重新扫描文件的额外工作。如果文件是大,可能会影响您的感知表现 - 如果这是一个问题,您应该考虑到这一点。

10

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

fileinput.lineno()

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

fileinput.filelineno()

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

+0

小警告:'fileinput'似乎不支持'with '在Python2.7中的语句... – Julien 2016-12-07 01:23:00

0

一种方式是遍历线,并保持已经看到的行数的明确计数:给予该

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

下面的代码创建了一个功能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.input()函数的正常行为?
  • 似乎该文件是在任何迭代启动之前首先完全读取的。如果是这样,对于非常大的文件,文件的大小可能会超过RAM的容量。我不确定这一点:我试图用1.5 GB的文件进行测试,但这个时间很长,我暂时放弃了这一点。如果这一点是正确的,则构成使用另一种解决方案的论据枚举()

为例:

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”或...

-1

关于solution by @eyquem,我建议使用mode='r'与文件输入模块和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() 
+1

这并没有提供问题的答案。要批评或要求作者澄清,在他们的帖子下留下评论 - 你总是可以评论你自己的帖子,一旦你有足够的[声誉](http://stackoverflow.com/help/whats-reputation),你会能够[评论任何帖子](http://stackoverflow.com/help/privileges/comment)。 - [评论] [发表评论](/ review/low-quality-posts/10786418) – Prune 2016-01-05 19:55:39

+0

@ Prune - 感谢您的评论,并且我在学习中加入了一段代码,以增加我的建议清晰度。 – speedchase 2016-01-05 21:36:39

+0

请注意,“以上”在答案中没有上下文。答案投票更改和答案可以以多种不同的方式进行排序。更好地链接到你所指的答案。 – ale 2016-01-06 00:43:54

7

下面的代码将打印的行号(其中,该指针是当前上),同时通过文件( '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 
... 
+0

请随时向您的代码添加一些解释... – andreas 2016-09-25 20:01:49

+0

感谢您的建议。我会 – 2016-09-26 11:24:37

0

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

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... 
相关问题