在Python 3.6中,如果有换行符,则需要更长的时间来读取文件。如果我有两个文件,一个有换行符,另一个没有换行符(否则它们有相同的文本),那么带换行符的文件大约需要100-200%的时间来读取。我提供了一个具体的例子。
sizeMB = 128
sizeKB = 1024 * sizeMB
with open(r'C:\temp\bigfile_one_line.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!\t'*73) # There are roughly 73 phrases in one KB
with open(r'C:\temp\bigfile_newlines.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!\n'*73)
%%timeit
with open(r'C:\temp\bigfile_one_line.txt', 'r') as f:
text = f.read()
1 loop, best of 3: 368 ms per loop
%%timeit
with open(r'C:\temp\bigfile_newlines.txt', 'r') as f:
text = f.read()
1 loop, best of 3: 589 ms per loop
这只是一个例子。我已经针对很多不同的情况对它进行了测试,他们做了同样的事情:
我的结论是,带有新行字符(' \ n')的文件比没有它们的文件需要更长的时间。但是,我希望所有角色都被视为相同。在读取大量文件时,这会对性能产生重要影响。 有谁知道为什么会这样?
我正在使用Python 3.6.1,Anaconda 4.3.24和Windows 10.
答案 0 :(得分:10)
当您在文本模式下使用Python打开文件(默认设置)时,它会使用它所调用的文件"通用换行符" (随PEP 278一起介绍,但稍后随着Python 3的发布而有所改变)。通用换行符的含义是,无论文件中使用何种换行符,您都只能在Python中看到\n
。因此,包含foo\nbar
的文件与包含foo\r\nbar
或foo\rbar
的文件相同(因为\n
,\r\n
和\r
都是行某些操作系统上使用的结束约定。)
提供支持的逻辑可能是导致性能差异的原因。即使文件中的\n
字符没有被转换,代码也需要比非换行符更仔细地检查它们。
如果您在二进制模式下打开文件而没有提供此类新行支持,我怀疑您看到的性能差异将会消失。您还可以在Python 3中将newline
参数传递给open
,这可能具有多种含义,具体取决于您给出的确切值。我不知道任何特定值会对性能产生什么影响,但是如果您看到的性能差异对您的程序真正重要,则可能值得测试。我尝试传递newline=""
和newline="\n"
(或者您平台的传统线路结尾)。
答案 1 :(得分:5)
但是,我希望所有角色都被视为相同。
嗯,他们不是。换行是特别的。
换行符并不总是表示为\n
。原因很长,可以追溯到早期的物理电传打字机,我不会进入这里,但故事结束的地方是Windows使用\r\n
,Unix使用{{1}和经典Mac OS曾经使用过\n
。
如果您以文字模式打开文件,文件使用的换行符会在您阅读时翻译为\r
,\n
将转换为您的操作系统行写作时打破常规。在大多数编程语言中,这是通过操作系统级代码动态处理的,并且相当便宜,但Python以不同的方式做事。
Python有一个名为universal newlines的功能,无论您使用什么操作系统,它都会尝试处理所有换行符约定。即使文件包含\n
,\r
和\n
换行符的混合,Python也会识别所有换行符并将其转换为\r\n
。除非您使用\n
参数newline
配置特定的行结束约定,否则Python 3中默认启用通用换行符。
在通用换行模式下,文件实现必须以二进制模式读取文件,检查open
个字符的内容,
construct a new string object with line endings translated
如果找到\r\n
或\r
行结尾。如果它只找到\r\n
个结尾,或者根本找不到行结尾,则不需要执行转换传递或构造新的字符串对象。
构造新字符串并翻译行结尾需要时间。使用选项卡读取文件,Python不必执行翻译。
答案 2 :(得分:3)
在Windows上,以文本模式打开时,写入时会将'\n'
个字符转换为'\r\n'
,而当读取时则相反。
所以,我做了一些实验。我现在在MacOS上,所以我的“原生”行结尾是'\n'
,所以我为你做了类似的测试,除了使用非原生的Windows行结尾:
sizeMB = 128
sizeKB = 1024 * sizeMB
with open(r'bigfile_one_line.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!!\t'*73) # There are roughly 73 phrases in one KB
with open(r'bigfile_newlines.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!\r\n'*73)
结果:
In [4]: %%timeit
...: with open('bigfile_one_line.txt', 'r') as f:
...: text = f.read()
...:
1 loop, best of 3: 141 ms per loop
In [5]: %%timeit
...: with open('bigfile_newlines.txt', 'r') as f:
...: text = f.read()
...:
1 loop, best of 3: 543 ms per loop
In [6]: %%timeit
...: with open('bigfile_one_line.txt', 'rb') as f:
...: text = f.read()
...:
10 loops, best of 3: 76.1 ms per loop
In [7]: %%timeit
...: with open('bigfile_newlines.txt', 'rb') as f:
...: text = f.read()
...:
10 loops, best of 3: 77.4 ms per loop
与您的非常相似,请注意,当我以二进制模式打开时,性能差异会消失。好的,如果相反,我使用* nix line-endings呢?
with open(r'bigfile_one_line_nix.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!\t'*73) # There are roughly 73 phrases in one KB
with open(r'bigfile_newlines_nix.txt', 'w') as f:
for i in range(sizeKB):
f.write('Hello World!\n'*73)
使用这些新文件的结果:
In [11]: %%timeit
...: with open('bigfile_one_line_nix.txt', 'r') as f:
...: text = f.read()
...:
10 loops, best of 3: 144 ms per loop
In [12]: %%timeit
...: with open('bigfile_newlines_nix.txt', 'r') as f:
...: text = f.read()
...:
10 loops, best of 3: 138 ms per loop
啊哈!性能差异消失了!所以是的,我认为使用非原生的行结尾会影响性能,这对于文本模式的行为是有意义的。