读取fi时不要转换换行符

2024-05-08 14:02:09 发布

您现在位置:Python中文网/ 问答频道 /正文

我在看一个文本文件:

f = open('data.txt')
data = f.read()

但是,data变量中的新行被规范化为LF('\n'),而文件包含CRLF('\r\n')。

如何指示Python按原样读取文件?


Tags: 文件txtreaddataopen规范化文本文件lf
3条回答

在Python 2.x中:

f = open('data.txt', 'rb')

正如the docs所说:

The default is to use text mode, which may convert '\n' characters to a platform-specific representation on writing and back on reading. Thus, when opening a binary file, you should append 'b' to the mode value to open the file in binary mode, which will improve portability. (Appending 'b' is useful even on systems that don’t treat binary and text files differently, where it serves as documentation.)

在Python3.x中,有三种选择:

f1 = open('data.txt', 'rb')

这将使换行保持未转换,但也将返回bytes,而不是str,您必须亲自将decode显式地返回到Unicode。(当然,2.x版本也返回了需要手动解码的字节(如果您想要Unicode的话),但在2.x版本中,str对象就是这样;在3.x版本中,str是Unicode的。)

f2 = open('data.txt', 'r', newline='')

这将返回str,并保留未翻译的换行符。然而,与2.x等价物不同,readline和friends将把'\r\n'视为换行符,而不是后跟换行符的正则字符。通常这无关紧要,但如果是的话,请记住。

f3 = open('data.txt', 'rb', encoding=locale.getpreferredencoding(False))

这与2.x代码处理新行的方式完全相同,并且返回str使用的编码与使用所有默认值时得到的编码相同…但在当前3.x中不再有效

When reading input from the stream, if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and these are translated into '\n' before being returned to the caller. If it is '', universal newlines mode is enabled, but line endings are returned to the caller untranslated.

需要为f3指定显式编码的原因是,以二进制模式打开文件意味着默认值从“使用locale.getpreferredencoding(False)解码”更改为“不解码,并返回原始bytes,而不是str”。同样,从the docs

In text mode, if encoding is not specified the encoding used is platform dependent: locale.getpreferredencoding(False) is called to get the current locale encoding. (For reading and writing raw bytes use binary mode and leave encoding unspecified.)

但是:

'encoding' … should only be used in text mode.

而且,至少从3.3开始,这是强制的;如果尝试使用二进制模式,则会得到ValueError: binary mode doesn't take an encoding argument

所以,如果您想编写同时在2.x和3.x上工作的代码,您使用什么?如果你想处理bytes,显然f和f1are the same. But if you want to deal instr, as appropriate for each version, the simplest answer is to write different code for each, probablyfandf2`。如果出现很多这样的情况,请考虑编写以下任一包装函数:

if sys.version_info >= (3, 0):
    def crlf_open(path, mode):
        return open(path, mode, newline='')
else:
    def crlf_open(path, mode):
        return open(path, mode+'b')

在编写多版本代码时要注意的另一件事是,如果您没有编写区域设置感知代码,locale.getpreferredencoding(False)在3.x中几乎总是返回一些合理的值,但在2.x中通常只返回'US-ASCII'。使用locale.getpreferredencoding(True)在技术上是不正确的,但如果你不想考虑编码的话,可能更像是你真正想要的。(尝试在2.x和3.x解释器中同时调用它,以查看原因或阅读文档。)

当然,如果你真的知道文件的编码,那总比猜测好。

在这两种情况下,'r'都表示“只读”。如果不指定模式,则默认值为'r',因此与默认值等效的二进制模式为'rb'

您需要以二进制模式打开文件:

f = open('data.txt', 'rb')
data = f.read()

'r'表示“读取”,'b'表示“二进制”)

然后一切都按原样返回,没有什么是正常化的

您可以使用codecs module来编写“版本不可知”代码:

Underlying encoded files are always opened in binary mode. No automatic conversion of '\n' is done on reading and writing. The mode argument may be any binary mode acceptable to the built-in open() function; the 'b' is automatically added.

import codecs
with codecs.open('foo', mode='r', encoding='utf8') as f:
    # python2: u'foo\r\n'
    # python3: 'foo\r\n'
    f.readline()

相关问题 更多 >