有时候,中文无法正常显示,如下所示都是乱码

python 中文 转unicode python中文转utf8_Desktop

这个时候,我们需要借助下 NotePad++ 工具,对整个日志文件进行转码,如下所示

我们发现,此文件用NotePad++ 打开,可以直接显示中文, 编码格式为 UTF-8编码

python 中文 转unicode python中文转utf8_python_02

我们使用快捷键 Ctrl+A 全选日志内容,选择【编码】–>【使用 UTF-8 BOM编码】,将日志内容切换编码,然后保存。

python 中文 转unicode python中文转utf8_Desktop_03

现在我们切换了编码为【UTF-8 BOM】之后,重新用TextAnalysisTool工具打开这份日志就可以正常显示中文了

python 中文 转unicode python中文转utf8_python 中文 转unicode_04


上面的方法,我们通过使用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)的区别。

  • 「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?网页代码一般使用哪个?
  • UTF-8和不带BOM的UTF-8有什么区别?
  • UTF-8编码格式的Byte Order Mark问题
  • UTF-8还是UTF-8 BOM???
  • UTF-8的BOM含义
  • 到底什么是UTF-8 BOM头
  • “UTF-8 with BOM“与 “UTF-8“ 的区别

结论:

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编码了。

python 中文 转unicode python中文转utf8_python 中文 转unicode_05

因此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,也就是说有没有这个标记。

python 中文 转unicode python中文转utf8_ico_06

但内容都一样 ,为什么相差了3个字节呢 ? 如下图 。

python 中文 转unicode python中文转utf8_python 中文 转unicode_07

多出来的 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('欧阳鹏 博客地址:')

# 加入BOM标记
#add_bom(file, codecs.BOM_UTF8)

# 打开测试文件,打印文件内容
with open(file, 'rb') as f:
    print(f.read())

python 中文 转unicode python中文转utf8_python_08

总共有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('欧阳鹏 博客地址:')

# 加入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\x9a'

[Done] exited with code=0 in 1.038 seconds

python 中文 转unicode python中文转utf8_python 中文 转unicode_09

使用 TextAnalysisTool 工具打开,乱码

python 中文 转unicode python中文转utf8_Desktop_10

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('欧阳鹏 博客地址:')

# 加入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\x9a'

[Done] exited with code=0 in 1.043 seconds

python 中文 转unicode python中文转utf8_ico_11

使用 TextAnalysisTool 工具打开,不会乱码了!

python 中文 转unicode python中文转utf8_Desktop_12

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\x9a'

[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\x9a'

[Done] exited with code=0 in 1.043 seconds

python 中文 转unicode python中文转utf8_ico_13

对比 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格式的文件,如下所示:

python 中文 转unicode python中文转utf8_python_14

文件打开是乱码的

python 中文 转unicode python中文转utf8_python_15

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>

python 中文 转unicode python中文转utf8_Desktop_16

  • 绝对路径
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>

python 中文 转unicode python中文转utf8_python 中文 转unicode_17

两种方式都可以识别,然后可以看到日志打印,添加了BOM标记头。我们可以看到C:\Users\000\Desktop\test_bom\test_utf-8.log的时间和其他的文件不同,以被修改。

python 中文 转unicode python中文转utf8_ico_18

打开文件内容查看,不乱码!

python 中文 转unicode python中文转utf8_Desktop_19

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>

python 中文 转unicode python中文转utf8_ico_20

我们可以看到该文件夹下所有的log日志文件都被修改。

打开该文件夹下所有的log日志文件,查看下内容不乱码!

python 中文 转unicode python中文转utf8_ico_21

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>

python 中文 转unicode python中文转utf8_ico_22

和传入指定文件夹路径的效果类似。

3.2 将Python脚本加入Windows右键菜单

写一个工具直接鼠标右键一键可以转换UTF-8编码转换成UTF-8 BOM编码,方便我在windows电脑上使用TextAnalysisTool工具来分析日志。

具体可以参考下面这个操作

  • 如何将一个Python脚本加入Windows右键菜单?

我们来看实际效果。

3.2.1 先写一个批处理文件

现在我们将这个命令封装成一个bat文件,这样我们以后就可以直接运行bat文件即可,不需要每次都敲命令了。

比如我们封装为 add_utf_bom.bat 文件,放在C:\Windows目录下,内容如下

python 中文 转unicode python中文转utf8_python 中文 转unicode_23

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 先写一个注册表文件

python 中文 转unicode python中文转utf8_python 中文 转unicode_24

add_utf_bom.reg文件的内容为:

Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\add_utf_bom\command]
@="add_utf_bom.bat \"%1\""

然后双击该注册表,即可导入注册表。

python 中文 转unicode python中文转utf8_python 中文 转unicode_25

点击 【是(Y)】,即可导入成功!

python 中文 转unicode python中文转utf8_Desktop_26

导入注册表成功之后,查询下注册表内容,如下所示:

python 中文 转unicode python中文转utf8_python 中文 转unicode_27

3.2.3 测试鼠标右键add_utf_bom菜单

python 中文 转unicode python中文转utf8_python_28

一闪而过,执行成功

python 中文 转unicode python中文转utf8_python 中文 转unicode_29

python 中文 转unicode python中文转utf8_Desktop_30

OK,整个需求完整实现,可以对指定的utf-8编码的文件,先使用鼠标右键菜单【add_utf_bom】实现添加BOM头的功能,然后再使用TextAnalysisTool工具来查看不乱码的日志文件!

四、再次扩展功能:添加完BOM头之后,直接用TextAnalysisTool工具打开此文件

4.1 待优化的现状

上面的步骤做完之后,我们可以很方便的给文件进行转码,给UTF-8格式的文件加上BOM头,使之可以使用TextAnalysisTool工具打开此文件后不乱码。 但是这样得进行两步骤

  1. 鼠标右键: 选择我们弄好的【add_utf_bom】菜单项,进行编码转换

python 中文 转unicode python中文转utf8_python_28

2. 重新打开此文件

python 中文 转unicode python中文转utf8_ico_32

略显麻烦,因此我想把两个步骤合并为一个,当点击鼠标右键: 选择我们弄好的【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:])

添加了标红的一段代码

python 中文 转unicode python中文转utf8_python_33

TextAnalysisTool 工具安装的路径为: D:\TextAnalysisTool.NET\TextAnalysisTool.NET.exe

python 中文 转unicode python中文转utf8_ico_34

添加的部分代码如下所示:

# 如果是单独打开某个文件,肯定是鼠标右键进来的,或者直接指定要打开这个文件的
            # 那么加上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 中文 转unicode python中文转utf8_python_35

转码中

python 中文 转unicode python中文转utf8_python 中文 转unicode_36

自动打开转码后的文件

python 中文 转unicode python中文转utf8_python 中文 转unicode_37

恩,到此为止,这个工具用起来才得心应手!

五、参考链接

  • 如何将一个Python脚本加入Windows右键菜单?
  • 【我的Android进阶之旅】使用TextAnalysisTool来快速提高你分析文本日志的效率
  • 【Adding BOM (unicode signature) while saving file in python】
  • python中使用指定exe打开指定的文件