前言
此前遇到过UTF8格式的文件有无BOM的导致的问题,最近在做自动化测试,读写配置文件时又遇到类似的问题,和此前一样,又是折腾了挺久之后,通过工具比较才知道原因。
两次在一个问题上面栽更头,就在想有没有一个一劳永逸的方法避免这个问题,或者能做到检测,不用到最后借助Beyond Compare进行16进制比较。
Python codecs
此前很少使用codecs,查阅了相关资料知道这个是一个好东西。
比如说当我们有数据要保存的时候,大多数时候会选择保存到TXT中,当然数据量大的时候,保存到数据库还是比较方便,然后在网络传输的时候需要序列号、json化。
而我们操作txt平常用得最多的就是open内置函数,或者file这个工厂函数,两者效果基本一样。
但是我们用open方法打开文件有时候会有一些问题,因为open打开文件只能写入str类型,而不会管字符串是什么编码方式。
例如这样是可以的,示例:
>>> fr = open('test.txt','a')
>>> line1 = "我爱祖国"
>>> fr.write(line1)
但是有时候我们爬虫或者其他方式得到一些数据写入文件时会有编码不统一的问题,此时写入open方式打开的文件就有问题了。示例:
>>> line2 = u'我爱祖国'
>>> fr.write(line2)
Traceback (most recent call last):
File "", line 1, in
fr.write(line2)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-11: ordinal not in range(128)
怎么办?我们可以将上面的line2编码成str类型。
操作步骤:先把原数据decode为unicode再encode为str,太麻烦了。。。
input文件(gbk, utf-8...) ----decode-----> unicode -------encode------> output文件(gbk, utf-8...)
其实Python提供了更简单的做法,那就是今天的主角codecs.open,示例:
>>> import codecs
>>> fw = codecs.open('test1.txt','a','utf-8')
>>> fw.write(line2)
>>>
没有报错,写入成功!
其实Python对多国语言的处理是支持的很好的,它可以处理当下任意编码的字符。
有一点需要清楚的是,当python要做编码转换的时候,会借助于内部的编码,转换过程是这样的:
原有编码 -> 内部编码 -> 目的编码
而codecs提供的方法可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述 操作。相对内置的open()来说,这个方法不容易在编码上出现问题。所以,推荐大家在文件读写的时候使用codecs
检测及消除BOM
然后继续我们今天的另外一个主题,怎么样消除UTF-8文件中的名BOM(Byte Order Mark),十六进制编码(EF BB BF),博主的方法有些取巧但是也比较高效,主要用到了codecs的函数BOM_UTF8,如果发现BOM_UTF8,则直接改写文件内容。
示例代码:
import codecs
with open(config_path) as source_file:
data = source_file.read()
# remove BOM
if data[:3] == codecs.BOM_UTF8: # 判断是否为带BOM文件
data = data[3:]
with codecs.open(config_path) as dest_file:
dest_file.write(data)
相关读写模式
codecs有如下的读写模式,和open用法基本一致。
模式
描述
r
仅读,待打开的文件必须存在
w
仅写,若文件已存在,内容将先被清空
a
仅写,若文件已存在,内容不会清空
r+
读写,待打开的文件必须存在
w+
读写,若文件已存在,内容将先被清空
a+
读写,若文件已存在,内容不会清空
rb
仅读,二进制,待打开的文件必须存在
wb
仅写,二进制,若文件已存在,内容将先被清空
ab
仅写,二进制,若文件已存在,内容不会清空
r+b
读写,二进制,待打开的文件必须存在
w+b
读写,二进制,若文件已存在,内容将先被清空
a+b
读写,二进制,若文件已存在,内容不会清空