不得不知道的Python字符串编码相关的知识
开发经常会遇到各种字符串编码的问题,例如报错SyntaxError: Non-ASCII character
'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
,又例如显示乱码。
由于之前不知道编码的原理,遇到这些情况,就只能不断的用各种编码decode和encode。。。。。
今天整理一个python中的各种编码问题的原因和解决方法,以后遇到编码问题,就不会像莽头苍蝇一样,到处乱撞了。
下面的python环境都是在2.7,听说在3.X中已经没有编码的问题了,因为所有的字符串都是unicode了,之后装个3.X试一下。
如果不知道什么是decode和encode,建议先看一下:这里
一、encoding的作用
1.在python文件中,如果有中文,就一定要在文件的第一行标记使用的编码类型,例如 #encoding=utf-8
,就是使用utf-8的编码,这个编码有什么作用呢?会改变什么呢?
demo1.py
# encoding=utf-8
test='测试test'
print type(test)
print repr(test)
输出:
<type 'str'>
'\xe6\xb5\x8b\xe8\xaf\x95test'
我们通过print把一个变量输出到终端的时候,IDE或者系统一般都会帮我们的输出作转换,例如中文字符会转成中文,所以就看不到变量的原始内容。
repr函数可以看这个变量的给python看的形式,也就是看到这个变量的原始内容
从上面的输出可以看到test变量的str类型,它的编码是utf-8的(怎么知道是utf-8,请看第三部分),也就是的encoding类型
如果我们把encoding改为gbk
demo2.py
# encoding=gbk
test='测试test'
print type(test)
print repr(test)
输出
<type 'str'>
'\xb2\xe2\xca\xd4test'
这样test的编码类型就变为gbk了。
所以这个encoding会决定在这个py文件中定义的字符串变量的编码方式。
而如果一个变量是从其他py文件导入,或者从数据库,redis等读取出来的话,它的编码又是怎样的?
a.py
# encoding=utf-8
test='测试test'
b.py
# encoding=gbk
from a import test
print repr(test)
输出
'\xe6\xb5\x8b\xe8\xaf\x95test'
a.py中定义test变量,a.py的编码方式是utf-8,b.py的编码方式是gbk,b从a中导入test,结果显示test依然为utf-8编码,也就是a.py的编码
所以encoding只会决定本py文件的编码方式,不会影响导入的或者从其他地方读取的变量的编码方式
二、常见报错codec can't encode characters
的原因
python的程序经常会报错 codec can't encode characters
或 codec can't decode characters
在python中定义一个字符串,
import sys
print sys.getdefaultencoding() # 输出 ascii
unicode_test=u'测试test'
print repr(str(unicode_test))
上面的代码会报错
'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
除了str方法外,如果操作两个都有中文的字符串,也会报错,但是只有其中一个有中文,却不会报错
unicode_test = u'测试test%s{0}'
print '%stest' % unicode_test # 不会报错
print '%s测试' % unicode_test #会报错
print unicode_test % 'test' #不会报错
print unicode_test % '测试' #会报错
print unicode_test.format('test') #不会报错
print unicode_test.format('测试') #会报错
print unicode_test.split('test') #不会报错
print unicode_test.split('测试') #报错
print unicode_test + 'test' #不会报错
print unicode_test + '测试' #会报错
为什么会这样?
这原因下面再解答,这里先列出这个报错的解决方法:
解决方法是:把系统的默认编码设置为utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print sys.getdefaultencoding()
unicode_test=u'测试test'
demo3.py
# encoding=utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
unicode_test=u'测试test'
utf8_test='测试test'
gbk_test=unicode_test.encode('gbk')
#合并unicode和utf-8
merge=unicode_test+utf8_test
print type(merge)
print repr(merge)
#合并unicode和gbk
merge=unicode_test+gbk_test
print type(merge)
print repr(merge)
print merge
#合并utf-8和gbk
merge=utf8_test+gbk_test
print type(merge)
print repr(merge)
print merge
这里定义三个分别是unicode,utf-8和gbk编码的字符串,unicode_test,utf8_test和gbk_test
1.合并unicode和utf-8的时候,输出:
<type 'unicode'>
u'\u6d4b\u8bd5test\u6d4b\u8bd5test'
合并的结果的编码是unicode编码。
2.合并unicode和gbk,会报错:
'utf8' codec can't decode byte 0xb2 in position 0: invalid start byte
所以我们可以推测:
在python对两个字符串进行操作的时候,如果这两个字符串有一个是unicode编码,有一个是非unicode编码,python会将非unicode编码的字符串decode成unicode编码,再进行字符串操作
例如合并字符串的操作可以写成以下的function:
def merge_str(str1, str2):
if isinstance(str1, unicode) and not isinstance(str2, unicode):
str2 = str2.decode(sys.getdefaultencoding())
elif not isinstance(str1, unicode) and isinstance(str2, unicode):
str1 = str1.decode(sys.getdefaultencoding())
return str1 + str2
PS:sys.getdefaultencoding()的初始值是ascii
所以,
codec can't encode(decode) characters
这个报错是encode或decode这两个方法产生的,而这个方法的参数是sys.getdefaultencoding()。如果用ascii编码对带有中文的字符串进行解码,就会报错。所以修改系统的默认编码可以避免这个报错。
当执行 str
操作时,python会执行 unicode_test.encode(sys.getdefaultencoding())
,所以也会报错。
3.#合并utf-8和gbk的时候却不会报错,python会直接把两个字符串合并,不会有decode或encode的操作,但是输出的时候,部分字符串会乱码。
demo4.py
# encoding=gbk
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
unicode_test = u'测试test'
utf8_test = unicode_test.encode('utf-8')
gbk_test = unicode_test.encode('gbk')
merge = utf8_test + gbk_test
print type(merge)
print repr(merge)
print merge
这里文件的encoding是gbk,sys.getdefaultencoding()设置为utf-8,结果是:
<type 'str'>
'\xe6\xb5\x8b\xe8\xaf\x95test\xb2\xe2\xca\xd4test'
测试test����test
即gbk的部分乱码了。所以输出的时候会按照sys.getdefaultencoding()的编码来解码。
三、怎么判断一个字符串(string)的编码方式
没有办法准确地判断一个字符串的编码方式,例如gbk的“\aa”代表甲,utf-8的“\aa”代表乙,如果给定“\aa”怎么判断是哪种编码?它既可以是gbk也可以是utf-8
我们能做的是粗略地判断一个字符串的编码方式,因为上面的例如的情况是很少的,更多的情况是gbk中的'\aa'代表甲,utf-8中是乱码,例如�,这样我们就能判断'\aa'是gbk编码,因为如果用utf-8编码去解码的结果是没有意义的
而我们经常遇到的编码其实主要的就只有三种:utf-8,gbk,unicode
- unicode一般是
\u
带头的,然后后面跟四位数字或字符串,例如\u6d4b\u8bd5
,一个\u
对应一个汉字 - utf-8一般是
\x
带头的,后面跟两位字母或数字,例如\xe6\xb5\x8b\xe8\xaf\x95\xe5\x95\x8a
,三个\x
代表一个汉字 - gbk一般是
\x
带头的,后面跟两位字母或数字,例如\xb2\xe2\xca\xd4\xb0\xa1
,两个个\x
代表一个汉字
- unicode一般是
使用chardet模块来判断
import chardet
raw = u'我是一只小小鸟'
print chardet.detect(raw.encode('utf-8'))
print chardet.detect(raw.encode('gbk'))
输出:
{'confidence': 0.99, 'encoding': 'utf-8'}
{'confidence': 0.99, 'encoding': 'GB2312'}
chardet模块可以计算这个字符串是某个编码的概率,基本对于99%的应用场景,这个模块都够用了。
四、string_escape和unicode_escape
1. string_escape
在str中,\x
是保留字符,表示后面的两位字符表示一个字符单元(暂且这么叫,不知道对不对),例如'\xe6'
,一般三个字符单元表示一个中文字符
所以在定义变量时,a='\xe6\x88\x91'
,是代表定义了一个中文字符“我”,但是有时候,我们不希望a这个变量代表中文字符,而是代表3*4=12个英文字符,可以使用encode('string_escape')
来转换:
'\xe6\x88\x91'.encode('string_escape')='\\xe6\\x88\\x91'
decode就是反过来。
转换前后的类型都是string。
还有一个现象,定义a='\x'
,a='\x0'
都是会报错ValueError: invalid \x escape
的,而定义a='\a'
,即反斜杠后面不是跟x,都会没问题,而定义a='\x00'
,即x后面跟两个字符,也是没问题的。
2. unicode_escape
同理在unicode中,\u
是保留字符,表示后面的四个字符表示一个中文字符,例如b=u'\u6211'
,表示“我:”,同理我们希望b变量,表示6个英文字符,而不是一个中文字符,就可以使用encode('unicode-escape')来转换:
u'\u6211'.encode('unicode-escape')='\u6211'
注意encode前是unicode,转换后是string。
在unicode中,\u是保留字符,但是在string中,就不是了,所以只有一个反斜杠,而不是两个。
decode就是反过来。
同理,a='\u'
也是会报错的
3. 例子
#正常的str和unicode字符
str_char='我'
uni_char=u'我'
print repr(str_char) # '\xe6\x88\x91'
print repr(uni_char) # u'\u6211'
# decode('unicode-escape')
s1='\u6211'
s2=s1.decode('unicode-escape')
print repr(s1) # '\\u6211'
print repr(s2) # u'\u6211'
# encode('unicode-escape')
s1=u'\u6211'
s2=s1.encode('unicode-escape')
print repr(s1) # u'\u6211'
print repr(s2) # '\\u6211'
# decode("string_escape")
s1='\\xe6\\x88\\x91'
s2=s1.decode('string_escape')
print repr(s1) # '\\xe6\\x88\\x91'
print repr(s2) # '\xe6\x88\x91'
# encode("string_escape")
s1='\xe6\x88\x91'
s2=s1.encode('string_escape')
print repr(s1) # '\xe6\x88\x91'
print repr(s2) # '\\xe6\\x88\\x91'
4. 应用
内容是unicode,但是type是str,就可以使用
decode("unicode_escape")
转换为内容和type都是unicodes1='\u6211'
s2=s1.decode('unicode-escape')
内容是str,但是type是unicode,就可以使用
encode("unicode_escape").decode("string_escape")
转换为内容和type都是strs1=u'\xe6\x88\x91'
s2=s1.encode('unicode_escape').decode("string_escape")
博文为作者原创,未经允许,禁止转载。
不得不知道的Python字符串编码相关的知识的更多相关文章
- 【转载】不得不知道的Python字符串编码相关的知识
原文地址:http://www.cnblogs.com/Xjng/p/5093905.html 开发经常会遇到各种字符串编码的问题,例如报错SyntaxError: Non-ASCII charact ...
- 你所不知道的Python | 字符串连接的秘密
字符串连接,就是将2个或以上的字符串合并成一个,看上去连接字符串是一个非常基础的小问题,但是在Python中,我们可以用多种方式实现字符串的连接,稍有不慎就有可能因为选择不当而给程序带来性能损失. 方 ...
- Python字符串的相关操作
1.大小写转换 判断字符串 s.isalnum() #所有字符都是数字或者字母 s.isalpha() #所有字符都是字母 s.isdigit() #所有字符都是数字 s.islower() #所有字 ...
- python 字符串编码
通过字符串的decode和encode方法 1 encode([encoding,[errors]]) #其中encoding可以有多种值,比如gb2312 gbk gb18030 bz2 zlib ...
- python字符串编码理解(转载)
(转载)字符编码和python使用encode,decode转换utf-8, gbk, gb2312 (http://www.cnblogs.com/jxzheng/p/5186490.html) A ...
- python字符串编码
python默认编码 python 2.x默认的字符编码是ASCII,默认的文件编码也是ASCII. python 3.x默认的字符编码是unicode,默认的文件编码是utf-8. 中文乱码问题 无 ...
- Python字符串编码——Unicode
ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte).也就是 ...
- 你可能不知道的 Python 技巧
英文 | Python Tips and Trick, You Haven't Already Seen 原作 | Martin Heinz (https://martinheinz.dev) 译者 ...
- 你所不知道的Python奇技淫巧
有时候你会看到很Cool的Python代码,你惊讶于它的简洁,它的优雅,你不由自主地赞叹:竟然还能这样写.其实,这些优雅的代码都要归功于Python的特性,只要你能掌握这些Pythonic的技巧,你一 ...
随机推荐
- JavaFX(三)窗口拖动
1.问题场景 在上一篇中,我们将窗口的默认标题栏隐藏从而导致鼠标点击窗体无法进行拖动. 2.解决思路 给组件添加鼠标按下事件监听器和鼠标拖动事件监听器. 3.代码实现 代码片段: private do ...
- AJAX XMLHttpRequest
<html> <head> <title>XMLHTTPRequest对象的说明DEMO</title> <script language=&qu ...
- MongoDB,HDFS, Spark to 电影推荐
http://www.infoq.com/cn/news/2014/12/mongdb-spark-movie-recommend MovieWeb是一个电影相关的网站,它提供的功能包括搜索电影信息. ...
- Adobe Edge Animate –EdgeCommons Log和全局变量设置功能
Adobe Edge Animate –EdgeCommons Log和全局变量设置功能 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. 一.Log功能 当 ...
- [转]How to convert IP address to country name
本文转自:http://www.codeproject.com/Articles/28363/How-to-convert-IP-address-to-country-name Download ...
- POJ 1159 - Palindrome (LCS, 滚动数组)
Palindrome Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 55018 Accepted: 19024 Desc ...
- Java反转单链表(code)
主要是面试中可能会经常碰上该类似操作,尤其是稍大点公司,面试官可能并不在乎你能不能搞定该题,但是这类型题目最是能体现程序员的思维状态 ---一个迷糊头脑的程序员 怎能立志改变这个世界 /** * @a ...
- codevs4373 窗口==poj2823 Sliding Window
Sliding Window Time Limit: 12000MS Memory Limit: 65536K Total Submissions: 53676 Accepted: 15399 ...
- echars3.0 柱状图y轴字体斜放
xAxis: [ { type: 'category', axisLabel: { interval: 0, rotate: 45,//倾斜角度设置,是什么时针未测 margin: 2 //距离上部的 ...
- ORACLE字符串分组聚合函数(字符串连接聚合函数)
ORACLE字符串连接分组串聚函数 wmsys.wm_concat SQL代码: select grp, wmsys.wm_concat(str) grp, 'a1' str from dual un ...