问题 Python file.tell()给出了奇怪的数字?


我在Windows 64bit上使用Python 3.3.0。

我有一个文本文件,如下所示:(见mediafire下载链接的底部)

hello

-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah


-data2:blah blah blah blah blah blah blah blah blah blah blah
-data3: Empty

-data4: Empty

我正试图浏览文件,因此我使用 .tell() 弄清楚我的立场是什么。但是,当读取如下所示的文件行时,我得到一个非常奇怪的结果:

f=open("test.txt")
while True:
    a = f.readline()
    print("{}    {}".format(repr(a),f.tell()))
    if a == "":
        break

结果:

'hello\n'    7
'\n'    9
'-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah bl
ah blah\n'    18446744073709551714
'\n'    99
'\n'    101
'-data2:blah blah blah blah blah blah blah blah blah blah blah\n'    164
'-data3: Empty\n'    179
'\n'    181
'-data4: Empty'    194
''    194

第3行的18446744073709551714是什么?虽然它看起来像一个不可能的价值, f.seek(18446744073709551714) 是一个可接受的值,显然确实把我带到了第3行的末尾。虽然,我似乎无法弄清楚为什么。

编辑: 以二进制模式打开不会产生任何问题 tell()

f=open("test.txt","rb")
while True:
    a = f.readline()
    print("{}    {}".format(repr(a),f.tell()))
    if a == b"":
        break

结果:

b'hello\r\n'    7
b'\r\n'    9
b'-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah b
lah blah\r\n'    97
b'\r\n'    99
b'\r\n'    101
b'-data2:blah blah blah blah blah blah blah blah blah blah blah\r\n'    164
b'-data3: Empty\r\n'    179
b'\r\n'    181
b'-data4: Empty'    194
b''    194

test.txt文本文件可以在这里下载,只需194个字节: http://www.mediafire.com/?1wm4lujb2j48y23


4853
2018-04-10 19:41


起源



答案:


这是由UNIX样式的行结尾引起的记录行为:

file.tell()

返回文件的当前位置,如 stdioftell()

注意:在Windows上, tell() 可以返回非法值(之后 fgets())在使用Unix风格的行结尾读取文件时。使用二进制模式   ('rb')来规避这个问题。


以上文档摘自python2.7.4文档。 python3的文档稍有改动,因为现在有一个处理I / O的类层次结构,我找不到这些信息。您的测试显示该行为无论如何都没有改变。另外python3.3的源代码有一个 XXX Windows support below is likely incomplete 在被调用的函数之前发表评论 tell


有一个 问题 在与此相关的python bug跟踪器中,Catalin Iacob的最终评论是:

我试图重现这一点,在我的磁盘上选择了一个文件,确实我得到了一个   负数,但该文件有Unix行结尾。这是   记录在 http://docs.python.org/2/library/stdtypes.html#file.tell   所以可能那时无事可做。

至于Armin在msg180145中的报告,即使它不直观,   这与ftell在Windows上的行为相匹配,如备注中所述   部分    http://msdn.microsoft.com/en-us/library/0ys3hc0b%28v=vs.100%29.aspx。   fileobjects上的tell()方法被明确记录为匹配   ftell行为:“返回文件的当前位置,就像stdio一样   ftell()“。所以即使它根本不直观,也可能   最好保持原样。 tell()返回直观的非零值   使用时在Python3和Python 2.7上使用'a'打开时的位置   io.open所以它无论如何都是固定的。

所以它似乎是一个“wontfix”错误。 有人应该开个问题(评论该问题)因为在python3文档中根本没有提到这个事实。


根据 Antoine Pitrou python3不使用 ftell() 所以,这似乎是一个不同的错误。此错误在python3.2.3中不可重现,并且可能在修复时引入 问题(至少,这是我能找到的唯一改变 tell() 在3.2.3和3.3之间)


最后编辑:根据 io 模块文档 tell 方法呢  返回自文件开头以来的字节数。返回的值是“不透明数字”,这意味着您可以使用它的唯一方法是将其传递给 seek 回到那个位置。其他操作没有意义。事实上,直到python3.2.3返回的值是你所期望的只是一个实现细节。

请注意该信息 这个 文档的一部分很简单 错误 并且,希望它将在未来得到修复。


10
2018-04-10 19:46



呃,等等,文本文件只是用记事本创建的。并且使用显示行结尾的notepad2,我看到CR + LF,这是Windows行的结尾吧? Unix行结尾只有LF吗?在修补我的测试文件之后,显然,更长的线路更容易出现这个问题。 - Eric
@Eric你是否尝试从python以二进制模式打开文件并查看内容?另外,你可以发布文件内容/上传文件,以便我们也可以测试吗? AFAIK我的唯一明智的解释,我坚信python的文件是正确的,所以我仍然认为这个问题只是根本调用 ftell() 正在回垃圾。 - Bakuriu
我在二进制模式下用同样的东西编辑了原始问题,并在mediafire中上传了文本文件。 - Eric
@Eric那么这是一个新的bug。查看更新的答案并将问题链接到错误跟踪器。 - Bakuriu
@Eric好的。根据python的devs,这不是一个bug。 tell 根本不做你的想法。更新了答案。 - Bakuriu


答案:


这是由UNIX样式的行结尾引起的记录行为:

file.tell()

返回文件的当前位置,如 stdioftell()

注意:在Windows上, tell() 可以返回非法值(之后 fgets())在使用Unix风格的行结尾读取文件时。使用二进制模式   ('rb')来规避这个问题。


以上文档摘自python2.7.4文档。 python3的文档稍有改动,因为现在有一个处理I / O的类层次结构,我找不到这些信息。您的测试显示该行为无论如何都没有改变。另外python3.3的源代码有一个 XXX Windows support below is likely incomplete 在被调用的函数之前发表评论 tell


有一个 问题 在与此相关的python bug跟踪器中,Catalin Iacob的最终评论是:

我试图重现这一点,在我的磁盘上选择了一个文件,确实我得到了一个   负数,但该文件有Unix行结尾。这是   记录在 http://docs.python.org/2/library/stdtypes.html#file.tell   所以可能那时无事可做。

至于Armin在msg180145中的报告,即使它不直观,   这与ftell在Windows上的行为相匹配,如备注中所述   部分    http://msdn.microsoft.com/en-us/library/0ys3hc0b%28v=vs.100%29.aspx。   fileobjects上的tell()方法被明确记录为匹配   ftell行为:“返回文件的当前位置,就像stdio一样   ftell()“。所以即使它根本不直观,也可能   最好保持原样。 tell()返回直观的非零值   使用时在Python3和Python 2.7上使用'a'打开时的位置   io.open所以它无论如何都是固定的。

所以它似乎是一个“wontfix”错误。 有人应该开个问题(评论该问题)因为在python3文档中根本没有提到这个事实。


根据 Antoine Pitrou python3不使用 ftell() 所以,这似乎是一个不同的错误。此错误在python3.2.3中不可重现,并且可能在修复时引入 问题(至少,这是我能找到的唯一改变 tell() 在3.2.3和3.3之间)


最后编辑:根据 io 模块文档 tell 方法呢  返回自文件开头以来的字节数。返回的值是“不透明数字”,这意味着您可以使用它的唯一方法是将其传递给 seek 回到那个位置。其他操作没有意义。事实上,直到python3.2.3返回的值是你所期望的只是一个实现细节。

请注意该信息 这个 文档的一部分很简单 错误 并且,希望它将在未来得到修复。


10
2018-04-10 19:46



呃,等等,文本文件只是用记事本创建的。并且使用显示行结尾的notepad2,我看到CR + LF,这是Windows行的结尾吧? Unix行结尾只有LF吗?在修补我的测试文件之后,显然,更长的线路更容易出现这个问题。 - Eric
@Eric你是否尝试从python以二进制模式打开文件并查看内容?另外,你可以发布文件内容/上传文件,以便我们也可以测试吗? AFAIK我的唯一明智的解释,我坚信python的文件是正确的,所以我仍然认为这个问题只是根本调用 ftell() 正在回垃圾。 - Bakuriu
我在二进制模式下用同样的东西编辑了原始问题,并在mediafire中上传了文本文件。 - Eric
@Eric那么这是一个新的bug。查看更新的答案并将问题链接到错误跟踪器。 - Bakuriu
@Eric好的。根据python的devs,这不是一个bug。 tell 根本不做你的想法。更新了答案。 - Bakuriu