V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Anybfans
V2EX  ›  问与答

请教一个关于 Pythin 2.7 中文乱码的问题。

  •  
  •   Anybfans · 2015-01-15 19:50:40 +08:00 · 3519 次点击
    这是一个创建于 3399 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在自学python,发现读取文件中的中文时候会出现问题。例如下面这道题。

    第 0012 题: 敏感词文本文件 filtered_words.txt,里面的内容 和 0011题一样,当用户输入敏感词语,则用 星号 * 替换,例如当用户输入「北京是个好城市」,则变成「**是个好城市」。

    这些是敏感词。
    北京
    程序员
    公务员
    领导
    牛比
    牛逼
    你娘
    你妈
    love
    sex
    jiangge

    def filter_words(words):
    #从文件中读取过滤单词名单
    file_object = open('filtered_words.txt','r')
    filtered_words = []
    for line in file_object:
    filtered_words.append(line.strip('\n'))
    file_object.close()
    print filtered_words

    发现输出结果是这样的。。写完后英文可以成功过略,但是中文就不行了。请问有什么解决方法么。。谢谢了
    ['\xb1\xb1\xbe\xa9', '\xb3\xcc\xd0\xf2\xd4\xb1', '\xb9\xab\xce\xf1\xd4\xb1', '\xc1\xec\xb5\xbc', '\xc5\xa3\xb1\xc8', '\xc5\xa3\xb1\xc6', '\xc4\xe3\xc4\xef', '\xc4\xe3\xc2\xe8', 'love', 'sex', 'jiangge']

    12 条回复    2015-01-15 21:08:26 +08:00
    Anybfans
        1
    Anybfans  
    OP
       2015-01-15 19:53:25 +08:00
    。。代码有缩进的。。发上来就不行了。。代码如下


    [img]http://anybfans.qiniudn.com/py.jpg[/img]
    cevincheung
        2
    cevincheung  
       2015-01-15 19:55:37 +08:00
    cn = u"中文"
    print cn
    Anybfans
        3
    Anybfans  
    OP
       2015-01-15 19:55:39 +08:00
    mV2GK
        4
    mV2GK  
       2015-01-15 20:02:57 +08:00
    python中文编码问题,utf-8,gbk,unicode。。。。

    可以看下下面的文章

    http://fc-lamp.blog.163.com/blog/static/1745666872012121240969/
    jyjmrlk
        5
    jyjmrlk  
       2015-01-15 20:10:09 +08:00   ❤️ 1
    这其实不是乱码啦,算是 Console 输出不友好。不信试试这个:

    https://gist.github.com/Masakichi/4316b3fd0f3c9350d597

    另外,楼主应该用的 Windows,默认 UTF-8 编码的话像注释里这样 decode encode 一下就好。当然这个和问题没有关系啦。
    Anybfans
        6
    Anybfans  
    OP
       2015-01-15 20:17:02 +08:00
    @jyjmrlk 先谢谢了。
    在代码注释头部已经添加了这个的
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-


    我意思是说。用户输入中文后,无法匹配到filtered_words.txt 里面的中文内容。英文的话可以成功过滤。
    Anybfans
        7
    Anybfans  
    OP
       2015-01-15 20:20:46 +08:00
    @mV2GK 》《 看了半天没发现list怎么转码。。从文件中读取内容的时候,读取到的中文就是\xb1\xb1\xbe\xa9这类的。 在头部注释已经给了# -*- coding: utf-8 -*-了。 先谢谢了
    jyjmrlk
        8
    jyjmrlk  
       2015-01-15 20:42:58 +08:00   ❤️ 1
    @Anybfans 我试过之后可以匹配啊,我刚指的编码是 Terminal 的编码。

    另外 len('北京 ') == 6 哦,len(u'北京 ') 才等于2

    至于编码问题吗,我的印象里是这样的

    str.decode('某种编码') => unicode

    unicode.encode('某种编码') => str

    只要某种编码选择没有问题,一般还好。
    Anybfans
        9
    Anybfans  
    OP
       2015-01-15 20:59:20 +08:00
    @jyjmrlk 嗯 谢谢。。我再研究一下。谢谢咯
    Sylv
        10
    Sylv  
       2015-01-15 21:01:17 +08:00   ❤️ 2
    你这里其实是有两个问题:

    第一个问题是你发现输出是这样 '\xb1\xb1\xbe\xa9',但这其实并不是乱码,这是非 ascii 字符串在 Python 内部的存储方式。例如 "北京" 在 utf-8 编码下在 Python 内部储存的就是 '\xe5\x8c\x97\xe4\xba\xac',你可以通过 repr() 方法取得这个,print repr("北京") -> '\xe5\x8c\x97\xe4\xba\xac'。因为你 print 的是个 list,Python 不会把 list 内的元素输出成供人阅读的字符串,而是打印出内部的存储形式。所以你的那个输出是正确的,没有乱码的问题。

    第二个问题就是既然没有乱码那为什么无法匹配?那是因为你的 filtered_words.txt 文件是用 Windows 下的默认 GBK 编码保存的,那么 Python 读取里面的内容也就是 GBK 编码的字符串,'\xb1\xb1\xbe\xa9' 就是 '北京' 的 GBK 编码的字节码。而你的 Python 脚本又用的是 UTF-8 的编码,估计你的输入也是 UTF-8 的编码的字符串,那么当然两个编码的字符串就无法匹配上了。同样都是 '北京','\xe5\x8c\x97\xe4\xba\xac' != '\xb1\xb1\xbe\xa9'。
    解决办法是,将读取的文本解码成 unicode,或者多一步再编码成 UTF-8 编码的 str。
    用 GBK 编码将输入解码成 unicode:line.decode('gbk') -> unicode。
    再用 UTF-8 编码将 unicode 编码成 UTF-8 编码的 str:line.decode('gbk').encode('utf-8')。
    如果你用来比对的输入也是 unicode 的话,就无需再编码成 str 了,'\xb1\xb1\xbe\xa9'.decode('gbk') = u'北京'。推荐你在内部都用 unicode 来处理,这样就能尽量避免 Python2 的编码坑。
    binux
        11
    binux  
       2015-01-15 21:01:27 +08:00
    什么是编码
    -------------

    对于计算机来说,它认识的就是01,称为 1bit,然后我们一般将 8bit 称为 1byte。那么这 1byte 能表示多少个字符呢? 2^8 = 256 种。那么问题来了,对于中文来说,上万个字,怎么表示得完啊。
    于是,就有了用多个 bytes 表示一个字的方法,这个表示方法就叫做编码。

    而编码有各种不同的标准,比如 gb2312 比如 gb18030 比如 utf8。当你看到一个二进制串的时候,你的告诉我,它是什么编码的,别人才能理解。所以单独给出一个二进制串,它是什么编码的,是没有办法知道的,只能猜。
    因为很重要,再说一遍,**一个二进制串是什么编码的,只能猜**,通过组合多个 bytes 看他是否在某个编码下合法,组合出来的字是否常见,猜!所以对于短文本,这是很不准的。甚至有一个二进制传,同时在 utf8 和 gb2312 下都是合法,合理的文字。

    所以,回到你这个问题,一般来说 gb2312 是 2bytes 的,而且几乎所有的 2bytes 都能转换成 gb2312,所以,当它是一个字的时候,chardet 不会猜它是 gb2312,但是两个字的时候就有可能会。


    什么是 unicode
    -------------
    这一堆编码有两个问题:

    1. 世界上有很多语言,有更多种编码
    2. gb2312 只有两字节,表示不完所有语言文字,而 utf8 是不定长的,你没法通过数二进制串知道它有几个字。

    为了解决这两个问题,unicode 出现了,unicode 设计能够在编码空间内(不准确),容纳所有语言的所有字符。并且它是定长的(不准确)。所以,在 Python 中,我们提倡所有文字都使用 unicode。

    但是,回到计算机体系中,计算机只认识 01,你是不能将一个 unicode 传递给别的系统的(不准确)。他们之间的传递,必须是二进制串。在这时,你必须将 unicode encode 为一个特定编码,而且这个编码是和对方系统约定好的。不然就会出现乱码,或者异常。这个过程发生在,例如:

    - print 输出
    - 将文字保存到文件
    - 储存数据库
    - 网络发包
    - 调用 shell 程序

    等等。

    **因为很重要再说一遍,你必须和对方系统约定正确的编码**。二进制串不指定编码,别人是无法理解的。
    Anybfans
        12
    Anybfans  
    OP
       2015-01-15 21:08:26 +08:00
    @Sylv 哇。。明白了。。终于明白啦。。非常感谢了。。我这就研究下。。
    @binux 非常感谢了。o(^▽^)o
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   6165 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 02:34 · PVG 10:34 · LAX 19:34 · JFK 22:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.