添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
仗义的手术刀  ·  Looking for a ...·  3 小时前    · 
失落的鸡蛋面  ·  2. 词法分析 — Python ...·  5 小时前    · 
暴走的电池  ·  python ...·  5 小时前    · 
面冷心慈的人字拖  ·  Issue 28637: Python ...·  10 小时前    · 
文雅的四季豆  ·  【马 ART 14-24mm F2.8 ...·  2 月前    · 

本文原载于 https://imlogm.github.io ,转载请注明出处~

摘要 :最近在做自然语言处理相关的项目,发现中文编码的问题实在需要好好学习下,我用python为例,简单介绍下python编程时如何处理好中文编码的问题。

关键字 :自然语言处理, 字符编码, python

1. 从字符编码谈起

讲真,字符编码是很大的一块内容,单用一篇博客是完全讲不完的。这里借用一下大佬的文章: 字符编码笔记:ASCII,Unicode 和 UTF-8 - 阮一峰的日志

看完上面的那篇文章之后,相信你对字符编码有了一定的认识。在中文的自然语言处理中,最常遇到的是ASCII,Unicode,UTF-8,GB2312,GBK等。这几种编码,你都可以搜索相关的文章看下,我这里就不展开介绍了。直接用几个python的程序解释下如何在python中处理字符编码的问题。

2. 关于python的str类型和print过程

比如一段程序:

1
2
3
4
5
6
# -*- coding:utf-8 -*-

s = "这是一段中文" # s是str类型的变量
print(s)

# 程序输出:“这是一段中文”

这段程序中的变量s就是str类型的。我们都知道计算机内部都是二进制的0和1,str类型就是这样的0和1组成的二进制字节流,也就是说这里的变量s在计算机内部是一段二进制字节,并不是字符串。

如果你是在python的交互式编程环境中,那么你可以做个实验:

1
2
3
>>> s = "这是一段中文"
>>> s
\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe6\xae\xb5\xe4\xb8\xad\xe6\x96\x87

\x 表示这个数是十六进制数, \xe8 表示这个数是十六进制数“E8”,转换为二进制为 11101000 ,上面的“\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe6\xae\xb5\xe4\xb8\xad\xe6\x96\x87”就是变量s所代表的字节流,换句话说,就是字符串“这是一段中文”在utf-8编码格式下的二进制表示。

这里就出现了两个问题:

  • 变量s赋值时,一段中文字符串是怎么变成二进制字节流的?
  • 打印变量s时,二进制字节流是怎么变成一段中文字符串的?
  • 首先是问题1。变量s赋值时,字符串经过某种编码方式编码(encode)成为二进制字节,再赋值给变量s。这里的“某种编码方式”由代码显式指出,代码的第一行 # -*- coding:utf-8 -*- 就是用来显式地告诉计算机,你在str类型赋值时,用utf-8的编码方式。

    然后是问题2。打印变量s时,二进制字节流通过某种编码方式解码(decode)为字符串。这里的“某种编码方式”由操作系统指出。我用的ubuntu系统使用的是utf-8的编码方式。

    注意体会 编码 解码 这两个词的不同。编码方式和解码方式一样,才能正常print,否则显示的是乱码。

    可能你还是不太明白,我们用上面的程序再做一组实验。因为每台电脑的命令行的编码方式不一样,我用的是ubuntu的系统,编码格式是utf-8,我以我的电脑为例来讲解。同时,注意实验要在命令行的状态下进行。有些ide比较智能,会自动更换输出环境的编码格式,达不到实验效果。

    第一个程序和上面的一样,我们来看下效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*- coding:utf-8 -*-

    # 命令行的编码方式为utf-8

    s = "这是一段中文" # s是str类型的变量,计算机把字符串以utf-8格式编码成二进制字节,赋值给s

    print(s) # s是str类型的变量,计算机读取s(也就是读取出二进制字节),然后以utf-8格式解码为字符串

    # 程序输出:“这是一段中文”

    然后,我们改动第一行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*- coding:GBK -*-

    # 命令行的编码方式为utf-8

    s = "这是一段中文" # s是str类型的变量,计算机把字符串以GBK格式编码成二进制字节,赋值给s

    print(s) # s是str类型的变量,计算机读取s(也就是读取出二进制字节),然后以utf-8格式解码为字符串

    # 程序输出:一段乱码

    我们再做第三个实验:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*- coding:utf-8 -*-

    # 命令行的编码方式为GBK

    s = "这是一段中文" # s是str类型的变量,计算机把字符串以utf-8格式编码成二进制字节,赋值给s

    print(s) # s是str类型的变量,计算机读取s(也就是读取出二进制字节),然后以GBK格式解码为字符串

    # 程序输出:一段乱码

    我想你应该能理解这三个程序之间的区别。

    2. 关于unicode类型

    unicode类型是python中的一种字符串类型,在计算机内也是二进制字节。不过不同于str是单纯的二进制字节,unicode类型特指由ucs2或者ucs4编码格式编码的二进制字节。

    如果你在python的交互式编程环境中,你可以做个实验:

    1
    2
    3
    >>> s = u"这是一段中文"    # 这边多了个u,表示变量s为unicode变量
    >>> s
    u'\u8fd9\u662f\u4e00\u6bb5\u4e2d\u6587'

    可以看到,和上面str类型的实验结果的 \x 不一样了,这里出现的是 \u \u 代表了在unicode编码表中的位置,比如 \u8fd9 就代表unicode编码表中8fd9这个位置的字符。

    python中unicode类型的变量是作为一个中转站存在的。比如你要把一段字符串从utf-8编码转为GBK编码,你需要做的是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*- coding:utf-8 -*-

    s = "这是一段中文" # s是str类型的变量,计算机把字符串以utf-8格式编码成二进制字节,赋值给s

    s.decode("utf-8") # 二进制字节s以utf-8格式解码到unicode,解码后s从str类型变为unicode类型

    s.encode("GBK") # unicode类型的变量s被以GBK格式编码为二进制字符串,编码后变量s从unicode类型变为str类型

    # 程序输出:一段乱码

    反过来,要把一个GBK编码的字符串转为utf-8也一样,要以unicode作为中转站。

    3. sys.setdefaultencoding(‘utf-8’)

    网上一些教程会教你,在遇到中文编码问题的时候,在代码的开头加上这几句:

    1
    2
    3
    4
    # -*- coding:utf-8 -*-
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')

    你一试,还真的解决了问题,但你不知道这几句话有什么用。

    第一句 # -*- coding:utf-8 -*- 上一段已经说了,是为了显式地说明代码是由utf-8格式编码的,如果你不加的话,一般来说是采用默认编码ascii。ascii不支持中文,你的代码中有任何中文就会出错。

    后面三句,最重要的是 sys.setdefaultencoding('utf-8') ,它的目的是修改默认的解码方式为utf-8。

    看下面的实验:

    1
    2
    3
    # -*- coding: utf-8 -*- 
    s = '中文字符' # s是字符串经过utf-8编码格式编码后的二进制字节,str类型
    s.encode('GBK') # s是二进制字节,它不会直接encode。python会首先调用decode,将s从str类型变为unicode格式,再用GBK编码为str类型

    你可以使用以下代码获取python默认的解码方式:

    1
    2
    import sys
    print(sys.getdefaultencoding())

    假如你获取到的默认解码方式为ascii。那么:

    1
    2
    3
    4
    5
    6
    # -*- coding: utf-8 -*- 
    s = "这是一段中文" # s是字符串经过utf-8编码格式编码后的二进制字节,str类型
    s.encode("GBK") # s是二进制字节,它不会直接encode。python会首先调用decode,将s从str类型变为unicode格式,再用GBK编码为str类型

    # 如果你的默认解码方式为ascii,那么上面一句话在实际执行时,相当于下面这句话
    s.decode("ascii").encode("GBK")

    显然,程序会报错:UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe4 in position 。

    解决方法1,显示地指明解码格式:

    1
    2
    3
    4
    # -*- coding: utf-8 -*- 
    s = "这是一段中文"

    s.decode("utf-8").encode("GBK")

    解决方法2,修改默认解码方式:

    1
    2
    3
    4
    5
    6
    7
    8
    # -*- coding: utf-8 -*- 
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')

    s = "这是一段中文"

    s.encode("GBK")

    你应该能从这几个实验中明白 sys.setdefaultencoding('utf-8') 的作用。

    4. 检验学习成果

    python常见编码错误集合 - 妙音的博客

    看看上面链接的博客里所列举的几个错误示例,现在你是否能够一眼就找出错误点,并给出解决方法呢?