Python学习 如何将UTF-8编码转换成UTF-8 BOM编码?
Posted 欧阳鹏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习 如何将UTF-8编码转换成UTF-8 BOM编码?相关的知识,希望对你有一定的参考价值。
一、需求描述
在我之前的博客:
【我的Android进阶之旅】使用TextAnalysisTool来快速提高你分析文本日志的效率
中有介绍下面一个关于中文无法显示的问题,如下所示:
有时候,中文无法正常显示,如下所示都是乱码
这个时候,我们需要借助下 NotePad++ 工具,对整个日志文件进行转码,如下所示
我们发现,此文件用NotePad++ 打开,可以直接显示中文,
编码格式为 UTF-8编码
我们使用快捷键 Ctrl+A 全选日志内容,选择【编码】–>【使用 UTF-8 BOM编码】,将日志内容切换编码,然后保存。
现在我们切换了编码为【UTF-8 BOM】之后,重新用TextAnalysisTool工具打开这份日志就可以正常显示中文了
上面的方法,我们通过使用NotePad++ 工具,来对整个日志文件进行转码。然后再进行查看,这样的步骤有点繁琐。有时候需要分析多份日志的时候就会显得很恶心。
所以在想,有没有其他的方式将utf-8编码转成utf-8 BOM编码
呢?
二、实现需求:将UTF-8编码转成UTF-8 BOM编码
我们可以使用python来实现这个需求。
2.1 了解下UTF-8 and UTF-8 BOM(Byte Order Mark)的区别
大家可以通过以下几个链接了解一下UTF-8 and UTF-8 BOM(Byte Order Mark)的区别。
结论:
UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。
所以不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯(顺便提一下:把带有 BOM 的小端序 UTF-16 称作「Unicode」而又不详细说明,这也是微软的习惯)。
什么是BOM
BOM(byte-order mark)
,即字节顺序标记,它是插入到以UTF-8、UTF16或UTF-32编码Unicode文件开头的特殊标记,用来识别Unicode文件的编码类型。
对于UTF-8来说,BOM并不是必须的,因为BOM用来标记多字节编码文件的编码类型和字节顺序(big-endian或little-endian)。
在绝大多数编辑器中都看不到BOM字符,因为它们能理解Unicode,去掉了读取器看不到的题头信息。若要查看某个Unicode文件是否以BOM开头,可以使用十六进制编辑器。下表列出了不同编码所对应的BOM。
BOM Encoding
EF BB BF UTF-8
FE FF UTF-16 (big-endian)
FF FE UTF-16 (little-endian)
00 00 FE FF UTF-32 (big-endian)
FF FE 00 00 UTF-32 (little-endian)
UTF-8以字节为编码单元因此不需要 BOM 来表明字节顺序,但可以用 BOM 来表明编码方式。字符 “Zero Width No-Break Space” 的 UTF-8 编码是 EF BB BF。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8编码了。
因此UTF-8编码的字符串开头处的三个bytes 0xef,0xbb,0xbf就称为UTF-8 BOM头。
BOM的来历
为了识别 Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个“特征符”或“字节顺序标记(byte-order mark,BOM)”来识别文件中使用的编码和字节顺序。
Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。
带BOM和不带BOM的区别
「UTF-8」和「带 BOM 的 UTF-8」的区别就是有没有 BOM。即文件开头有没有 U+FEFF,也就是说有没有这个标记。
但内容都一样 ,为什么相差了3个字节呢 ? 如下图 。
多出来的 ef bb bf 就是上面相差三个字节的原因 。
BOM——Byte Order Mark,就是字节序标记
bom是为utf-16和utf-32准备的,用于标记字节顺序。微软在utf-8中使用bom是因为这样可以把UTF-8和ASCII等编码区分开来,但这样的文件在windows之外的操作系统里会带来问题。
带还是不带?
如果你的编程平台需要跨平台编译,比如,会在linux平台上编译,而不是只在windows上运行,建议不带BOM,unicode标准就是不带,带BOM毕竟那是微软的那一套,带了会出现很大的问题。反之,如果你的程序只在windows平台上编译出windows程序,这个可有可无。
注意:这里所说的带还是不带,指的是:源码字符集(the source character set)-源码文件是使用何种编码保存的;
现在linux平台下的GCC 4.6及以上的版本已经可以支持带BOM的源码了!!!!!
所以之前出现的问题也可以不用冲突,带或者不带,以后就不会成为一个问题。
2.2 实现需求
参考链接: 【Adding BOM (unicode signature) while saving file in python】
import codecs
# https://stackoverflow.com/questions/5202648/adding-bom-unicode-signature-while-saving-file-in-python
""" 给指定文件,添加BOM标记
参数:
file: 文件
bom: BOM标记
"""
def add_bom(file, bom: bytes):
with open(file, 'r+b') as f:
org_contents = f.read()
f.seek(0)
f.write(bom + org_contents)
# 定义一个测试文件,名为test.log
file = 'test.log'
# 打开 测试文件,使用utf-8编码写入一段中英文混杂的内容
with open(file, 'w', encoding='utf-8') as f: # without BOM
f.write('欧阳鹏 博客地址:https://blog.csdn.net/ouyang_peng')
# 加入BOM标记
#add_bom(file, codecs.BOM_UTF8)
# 打开测试文件,打印文件内容
with open(file, 'rb') as f:
print(f.read())
总共有4个区域:
- 区域1
定义 add_bom方法,给指定文件,添加BOM标记 - 区域2
定义一个测试文件,名为test.log,打开 测试文件,使用utf-8编码写入一段中英文混杂的内容 - 区域3
给测试文件,加入BOM标记 - 区域4
打印测试文件的内容
2.2.1 测试写入utf-8内容并读取打印
import codecs
# https://stackoverflow.com/questions/5202648/adding-bom-unicode-signature-while-saving-file-in-python
""" 给指定文件,添加BOM标记
参数:
file: 文件
bom: BOM标记
"""
def add_bom(file, bom: bytes):
with open(file, 'r+b') as f:
org_contents = f.read()
f.seek(0)
f.write(bom + org_contents)
# 定义一个测试文件,名为test.log
file = 'test.log'
# 打开 测试文件,使用utf-8编码写入一段中英文混杂的内容
with open(file, 'w', encoding='utf-8') as f: # without BOM
f.write('欧阳鹏 博客地址:https://blog.csdn.net/ouyang_peng')
# 加入BOM标记
#add_bom(file, codecs.BOM_UTF8)
# 打开测试文件,打印文件内容
with open(file, 'rb') as f:
print(f.read())
运行程序,如下所示:
[Running] python -u "c:\\Users\\000\\Desktop\\add_utf8_bom.py"
b'\\xe6\\xac\\xa7\\xe9\\x98\\xb3\\xe9\\xb9\\x8f \\xe5\\x8d\\x9a\\xe5\\xae\\xa2\\xe5\\x9c\\xb0\\xe5\\x9d\\x80\\xef\\xbc\\x9ahttps://blog.csdn.net/ouyang_peng'
[Done] exited with code=0 in 1.038 seconds
使用 TextAnalysisTool 工具打开,乱码
2.2.2 测试写入utf-8内容,并给文件加入BOM标记,然后读取打印
import codecs
# https://stackoverflow.com/questions/5202648/adding-bom-unicode-signature-while-saving-file-in-python
""" 给指定文件,添加BOM标记
参数:
file: 文件
bom: BOM标记
"""
def add_bom(file, bom: bytes):
with open(file, 'r+b') as f:
org_contents = f.read()
f.seek(0)
f.write(bom + org_contents)
# 定义一个测试文件,名为test.log
file = 'test.log'
# 打开 测试文件,使用utf-8编码写入一段中英文混杂的内容
with open(file, 'w', encoding='utf-8') as f: # without BOM
f.write('欧阳鹏 博客地址:https://blog.csdn.net/ouyang_peng')
# 加入BOM标记
add_bom(file, codecs.BOM_UTF8)
# 打开测试文件,打印文件内容
with open(file, 'rb') as f:
print(f.read())
输出内容为:
[Running] python -u "c:\\Users\\000\\Desktop\\add_utf8_bom.py"
b'\\xef\\xbb\\xbf\\xe6\\xac\\xa7\\xe9\\x98\\xb3\\xe9\\xb9\\x8f \\xe5\\x8d\\x9a\\xe5\\xae\\xa2\\xe5\\x9c\\xb0\\xe5\\x9d\\x80\\xef\\xbc\\x9ahttps://blog.csdn.net/ouyang_peng'
[Done] exited with code=0 in 1.043 seconds
使用 TextAnalysisTool 工具打开,不会乱码了!
2.2.2 对比输出内容
[Running] python -u "c:\\Users\\000\\Desktop\\add_utf8_bom.py"
b'\\xe6\\xac\\xa7\\xe9\\x98\\xb3\\xe9\\xb9\\x8f \\xe5\\x8d\\x9a\\xe5\\xae\\xa2\\xe5\\x9c\\xb0\\xe5\\x9d\\x80\\xef\\xbc\\x9ahttps://blog.csdn.net/ouyang_peng'
[Done] exited with code=0 in 1.038 seconds
[Running] python -u "c:\\Users\\000\\Desktop\\add_utf8_bom.py"
b'\\xef\\xbb\\xbf\\xe6\\xac\\xa7\\xe9\\x98\\xb3\\xe9\\xb9\\x8f \\xe5\\x8d\\x9a\\xe5\\xae\\xa2\\xe5\\x9c\\xb0\\xe5\\x9d\\x80\\xef\\xbc\\x9ahttps://blog.csdn.net/ouyang_peng'
[Done] exited with code=0 in 1.043 seconds
对比 UTF-8 和 UTF-8 BOM的内容,可以发现,多了\\xef\\xbb\\xbf
三个字节,因此UTF-8编码的字符串开头处的三个bytes 0xef,0xbb,0xbf就称为UTF-8 BOM头。
三、扩展
3.1 扩展为可实际使用的脚本
上面的demo我们测试完毕之后,我们扩展一下,使之扩展为可实际使用的脚本。
add_utf8_bom.py
的代码如下,
import sys
import os
import glob
import codecs
""" 给指定文件,添加BOM标记
参数:
file: 文件
bom: BOM标记
"""
def add_bom(file, bom: bytes):
print("now,add_bom for file :[%s] ,with bom:[%s]" %(file,bom))
with open(file, 'r+b') as f:
org_contents = f.read()
f.seek(0)
f.write(bom + org_contents)
""" 根据给定的不同参数,给指定文件,添加BOM标记
参数:
args 传入进来的参数。
如果参数只有一个,则区分是目录还是文件,
如果是文件夹,则遍历文件夹中的.log日志文件,然后给每个.log日志文件添加BOM标记
如果是文件则直接添加BOM标记。
如果不传参数,则直接遍历当前目前下的所有.log日志文件
"""
def main(args):
if 1 == len(args):
if os.path.isdir(args[0]):
filelist = glob.glob(args[0] + "/*.log")
for filepath in filelist:
add_bom(filepath, codecs.BOM_UTF8)
else:
add_bom(args[0], codecs.BOM_UTF8)
else:
filelist = glob.glob("*.log")
for filepath in filelist:
add_bom(filepath, codecs.BOM_UTF8)
if __name__ == "__main__":
main(sys.argv[1:])
我们来做一下测试,我们准备一个目录 C:\\Users\\000\\Desktop\\test_bom
,放好写好的脚本,以及4个utf-8格式的文件,如下所示:
文件打开是乱码的
3.1.1 传入一个指定文件路径
如下所示:指定传入一个指定的文件名。文件名可以传入相对路径或者绝对路径。
- 相对路径
PS C:\\Users\\000> cd "c:\\Users\\000\\Desktop\\test_bom"
PS C:\\Users\\000\\Desktop\\test_bom> python -u "c:\\Users\\000\\Desktop\\test_bom\\add_utf8_bom.py" "test_utf-8.log"
now,add_bom for file :[test_utf-8.log] ,with bom:[b'\\xef\\xbb\\xbf']
PS C:\\Users\\000\\Desktop\\test_bom>
- 绝对路径
PS C:\\Users\\000\\Desktop\\test_bom> python -u "c:\\Users\\000\\Desktop\\test_bom\\add_utf8_bom.py" "C:\\Users\\000\\Desktop\\test_bom\\test_utf-8.log"
now,add_bom for file :[C:\\Users\\000\\Desktop\\test_bom\\test_utf-8.log] ,with bom:[b'\\xef\\xbb\\xbf']
PS C:\\Users\\000\\Desktop\\test_bom>
两种方式都可以识别,然后可以看到日志打印,添加了BOM标记头。
我们可以看到C:\\Users\\000\\Desktop\\test_bom\\test_utf-8.log
的时间和其他的文件不同,以被修改。
打开文件内容查看,不乱码!
3.1.2 传入一个指定文件夹路径
传入一个指定文件夹路径,如下所示,传入文件夹路径为:C:\\Users\\000\\Desktop\\test_bom\\
PS C:\\Users\\000\\Desktop\\test_bom> python -u "c:\\Users\\000\\Desktop\\test_bom\\add_utf8_bom.py" "C:\\Users\\000\\Desktop\\test_bom\\"
now,add_bom for file :[C:\\Users\\000\\Desktop\\test_bom\\test_utf-8 (1).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[C:\\Users\\000\\Desktop\\test_bom\\test_utf-8 (2).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[C:\\Users\\000\\Desktop\\test_bom\\test_utf-8 (3).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[C:\\Users\\000\\Desktop\\test_bom\\test_utf-8.log] ,with bom:[b'\\xef\\xbb\\xbf']
PS C:\\Users\\000\\Desktop\\test_bom>
我们可以看到该文件夹下所有的log日志文件都被修改。
打开该文件夹下所有的log日志文件,查看下内容不乱码!
3.1.3 不传入指定路径
不传入指定路径,则会遍历当前路径下所有的.log日志文件。
PS C:\\Users\\000\\Desktop\\test_bom> python -u "c:\\Users\\000\\Desktop\\test_bom\\add_utf8_bom.py"
now,add_bom for file :[test_utf-8 (1).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[test_utf-8 (2).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[test_utf-8 (3).log] ,with bom:[b'\\xef\\xbb\\xbf']
now,add_bom for file :[test_utf-8.log] ,with bom:[b'\\xef\\xbb\\xbf']
PS C:\\Users\\000\\Desktop\\test_bom>
和传入指定文件夹路径的效果类似。
3.2 将Python脚本加入Windows右键菜单
写一个工具直接鼠标右键一键可以转换UTF-8编码转换成UTF-8 BOM编码,方便我在windows电脑上使用TextAnalysisTool工具来分析日志。
具体可以参考下面这个操作
我们来看实际效果。
3.2.1 先写一个批处理文件
现在我们将这个命令封装成一个bat文件,这样我们以后就可以直接运行bat文件即可,不需要每次都敲命令了。
比如我们封装为 add_utf_bom.bat
文件,放在C:\\Windows目录下,内容如下
add_utf_bom.bat
文件源代码如下:
C:\\Python39\\python.exe C:\\Windows\\add_utf8_bom.py %*
- 第一个参数 C:\\Python39\\python.exe 表示要运行的python程序的路径,上面的python.exe文件在c:\\Python39目录下,如下所示
如果你的python文件不是这个目录,改为你自己的python安装目录。
- 第二个参数 C:\\Windows\\add_utf8_bom.py 表示添加BOM标记的python脚本路径
3.2.2 先写一个注册表文件
add_utf_bom.reg文件的内容为:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\\*\\shell\\add_utf_bom\\command]
@="add_utf_bom.bat \\"%1\\""
然后双击该注册表,即可导入注册表。
点击 【是(Y)】,即可导入成功!
导入注册表成功之后,查询下注册表内容,如下所示:
3.2.3 测试鼠标右键add_utf_bom菜单
一闪而过,执行成功
OK,整个需求完整实现,可以对指定的utf-8编码的文件,先使用鼠标右键菜单【add_utf_bom】实现添加BOM头的功能,然后再使用TextAnalysisTool工具来查看不乱码的日志文件!
四、再次扩展功能:添加完BOM头之后,直接用TextAnalysisTool工具打开此文件
4.1 待优化的现状
上面的步骤做完之后,我们可以很方便的给文件进行转码,给UTF-8格式的文件加上BOM头,使之可以使用TextAnalysisTool工具打开此文件后不乱码。
但是这样得进行两步骤
- 鼠标右键: 选择我们弄好的【add_utf_bom】菜单项,进行编码转换
2. 重新打开此文件
略显麻烦,因此我想把两个步骤合并为一个,当点击鼠标右键: 选择我们弄好的【add_utf_bom】菜单项,进行编码转换之后,直接使用TextAnalysisTool工具打开此文件。
4.2 实现功能
因此我们修改下python脚本即可。
import sys
import os
import glob
import codecs
""" 给指定文件,添加BOM标记
参数:
file: 文件
bom: BOM标记
"""
def add_bom(file, bom: bytes):
print("now,add_bom for file :[%s] ,with bom:[%s]" %(file,bom))
with open(file, 'r+b') as f:
org_contents = f.read()
f.seek(0)
f.write(bom + org_contents)
""" 根据给定的不同参数,给指定文件,添加BOM标记
参数:
args 传入进来的参数。
如果参数只有一个,则区分是目录还是文件,
如果是文件夹,则遍历文件夹中的.log日志文件,然后给每个.log日志文件添加BOM标记
如果是文件则直接添加BOM标记。
如果不传参数,则直接遍历当前目前下的所有.log日志文件
"""
def main(args):
if 1 == len(args):
if os.path.isdir(args[0]):
filelist = glob.glob(args[0] + "/*.log")
for filepath in filelist:
add_bom(filepath, codecs.BOM_UTF8)
else:
add_bom(args[0], codecs.BOM_UTF8)
# 如果是单独打开某个文件,肯定是鼠标右键进来的,或者直接指定要打开这个文件的
# 那么加上BOM标记之后,直接使用TextAnalysisTool工具直接打开
# TextAnalysisTool 工具安装的路径为: D:\\TextAnalysisTool.NET\\TextAnalysisTool.NET.exe
# 所以直接打开此文件
# TextAnalysisTool 安装目录为: D:\\TextAnalysisTool.NET
# TextAnalysisTool 程序名为: TextAnalysisTool.NET.exe
# 要打开的文件为: args[0] 传进来的参数
os.system('start "" /d "D:\\TextAnalysisTool.NET" /wait "TextAnalysisTool.NET.exe" %s' %args[0])
else:
filelist = glob.glob("*.log")
for filepath in filelist:
add_bom(filepath, codecs.BOM_UTF8)
if __name__ == "__main__":
main(sys.argv[1:])
添加了标红的一段代码
TextAnalysisTool 工具安装的路径为: D:\\TextAnalysisTool.NET\\TextAnalysisTool.NET.exe
添加的部分代码如下所示:
# 如果是单独打开某个文件,肯定是鼠标右键进来的,或者直接指定要打开这个文件的
# 那么加上BOM标记之后,直接使用TextAnalysisTool工具直接打开
# TextAnalysisTool 工具安装的路径为: D:\\TextAnalysisTool.NET\\TextAnalysisTool.NET.exe
# 所以直接打开此文件
# TextAnalysisTool 安装目录为: D:\\TextAnalysisTool.NET
# TextAnalysisTool 程序名为: TextAnalysisTool.NET.exe
# 要打开的文件为: args[0] 传进来的参数
os.system('start "" /d "D:\\TextAnalysisTool.NET" /wait "TextAnalysisTool.NET.exe" %s' %args[0])
4.3 测试一键转码并打开功能
转码中
自动打开转码后的文件
恩,到此为止,这个工具用起来才得心应手!
五、参考链接
以上是关于Python学习 如何将UTF-8编码转换成UTF-8 BOM编码?的主要内容,如果未能解决你的问题,请参考以下文章