字符编码与编程语言
2016-09-16 21:41
190 查看
python2.*
Python2.x中对于字符串有两种表示方法,str和unicode,unicode更像是一个复杂类型,通常表示一个Unicode对象。而str则是一个基础类型,它仅仅表示字符数组([]byte)。因为str仅仅是字符数组,因此,即使你把某一段中文使用UTF-8编码的时候,它的存在形式还是字符数组,当你使用len内置方法去求值的时候,你看到的肯定不是中文汉字的个数。这个时候怎么办呢?我们通常会把它转成unicode对象,然后进行操作,这样得到的结果就回是我们预期的。
Python的默认字符集在几个大版本中有过改变,以下是各个版本的默认字符集列举:
Python2.1及以前: latin1
Python2.3及之后,Python2.5以前:latin1 (但是会对非ASCII字符集字符提出WARNING)
Python2.5及以后:ASCII
几种编码情况分析:
脚本字符编码
指脚本文件本身是用何种字符编码的,默认情况Python解释器认为脚本是ascii码:
# test.py print "你好"
上面是test.py脚本,运行 python test.py 会报错
解决方法:
在第一行或第二行进行编码声明
格式须符合
"coding[:=]\s*([-\w.]+)"
更详细请参考pep-0263
# coding=utf-8 # -*- coding: utf-8 -*- # vim: set fileencoding=utf-8
使用上面三种格式之一,就相当于告诉python解释器使用utf8编码解释脚本文件。
当然也会出现编码格式说明与实际编码格式不一致的情况,这样就会导致问题,一般很少出现
解释器字符编码:
解释器字符编码是指解释器内部认为的str类型的字符串的编码,也就是说python解释器会把str类型的字符串当作何种字符编码来处理。默认,python解释器字符编码也是ascii的。可以通过命令查看:
>>> sys.getdefaultencoding() 'ascii'
在windows下和Linux下的字符编码显示:
# windows >>> 'A' 'A' >>> '你好' '\xc4\xe3\xba\xc3' >>>
# linux >>> 'A' 'A' >>> '你好' '\xe4\xbd\xa0\xe5\xa5\xbd'
\x意味着接下去的两个字符以十六进制解释为字符,如\xaa其意思即为chr(0xaa),而chr是把数字转换为字符串,如chr(0x41)或chr(65)结果为'A'
因为ASCII码中只有0-127,所以多出的无法显示为字符,因此只能显示十六进制格式的
在windows下默认为ANSI,在中文下为gbk(或者更高,因为是向下兼容的,所以以gbk代替),而gbk表示一个中文字符为2个字节;而在Linux下默认为uft-8格式的,‘你好’为6个字节,所以结果不同
输入
输入 : raw_input只接收str类型字符串,不接收unicode类型
>>> s=raw_input(u'你好') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordin al not in range(128)
这里就要说到Python的隐式转换,当一个str类型变量和一个unicode类型变量进行连接操作的时候,或者对一个str对象使用encode方法的时候,Python内部都会尝试将当前的str对象转换成unicode对象,然后在进行操作。那当把str对象转换成unicode对象的时候,采用什么编码呢?问题就在于此。它会采用sys.getdefaultencoding()方法返回的编码方式,很不幸的是,往往返回的是都是ascii。而实际上你的str对象里面保存的是UTF-8编码的字符数组,而Python默认却会使用ascii去转换,这个时候就报出上面的错误了。
只要去掉u就好,则汉字依旧为str,只不过编码为gbk;这个Unicode转换问题还经常发生在输出的时候,在使用print的时候,如果同时输出Unicode和str,例如:
print "%s, %s" % (u'Tom','你好')
往往会发生str到Unicode的类型转化,而如果str中含有汉字的话,会报错,解决方法是输出都使用Unicode字符串
输出
从上面其实可以看出,在命令中输入一个字符,其格式是操作系统的编码格式,而输出呢?也一样,是本地格式
在命令行中, print打印对象的_str_(),不用print显示的是对象的_repr_(),在脚本中后者可用 print repr() 的形式来看
补充:
__str__()强调可读性,所以更多服务于用户;
__repr__()强调准确性,更多服务于开发者;很多情况下
__str__()会直接使用
__repr__(),更加详细的区别可参考difference-between-str-and-repr-in-python
先补充个知识:
不同的字符编码集如utf-8、gbk等等的字符串之间相互转换是怎么进行的呢?
如果单独写两个不同字符集的转换,不同字符编码集多,且不具备扩展性
如果要是都转成一个共同的,再转的话,就方便很多,每个编码就只负责自己的工作就行
而unicode几乎涵盖了一切字符串,非常适合做这个桥梁,其相关的两个方法是decode和encode。
强调:Unicode是一种单独的字符串类型,与str不同;所以其实encode和decode就是类型转换;使用encode,转成的都是str对象,可以是utf-8格式,而这些格式都可以用ASCII码来表示,只不过无法表示(127以上)的直接用十六进制数代替了
# windows # test.py # coding = 'utf-8' s = '你好' print s.decode('uft8') print s
而结果却是
python test.py 你好 浣犲ソ
为什么呢?
如果你能理解前面所述的,结果就很容易解释了,s为uft-8格式的,所以直接解码为Unicode可以输出成功,但是如果直接print的话,是用gbk解码,所以就错了,详细解释如下
直接解码为Unicode,字符串为
u'\u4f60\u597d'(就是
u"你好"的另一种形式),这个表示格式类似于前面的\x,不过是4个字节;其用Unicode来解释,而不是ASCII码,所以能直接输出成功
'\xe4\xbd\xa0\xe5\xa5\xbd'这是‘你好’的utf-8,可以试试使用gbk对两个字母分别解码,其结果就是那三个怪异的中文;
最后,因为Linux的默认编码为utf-8,所以不存在这个问题
最后的最后,请不要使用windows下的记事本:一是默认会存为ANSI(gbk),二是就算存为utf-8,也是带BOM的,容易出错
如果使用pycharm的话,结果会有所不同,这是因为pycharm的默认project编码是utf-8;改为gbk后结果与上述相同
encode和decode的补充
似乎有了unicode对象的encode方法和str的decode方法就足够了。奇怪的是,unicode也有decode,而str也有 encode,到底这两个是干什么的。
str的encode其实就是decode为Unicode,然后encode,问题是decode一般使用默认的sys.getdefaultencoding()(一般是ascii),所以会常常出错
反之亦然,unicode对象使用decode是先使用ASCII(默认值)进行encode,再用指定的编码方式解码为Unicode
这两种方式一般不建议使用
考验
str(s)和unicode(s)是两个工厂方法,分别返回str字符串对象和unicode字符串对象,str(s)等效于s.encode(‘ascii’),unicode(s)等效于s.decode(‘ascii’)
s3 = u"你好" str(s3) s4 = "你好" unicode(s4)
上述这两个都会出错,你知道为什么吗?
中文字符没有对应的ASCII码,如果你去了u,就可以,其输出结果为s3的gbk字节码
s4类型不是ASCII
在廖雪峰的Python学习网站2.7中字符串与编码的回复中有不少的中文输入输出问题,可以尝试去解决
文件
内置的open()方法打开文件时,read()读取的是str,读取后需要使用正确的编码格式进行decode()。write()写入时,如果参数是unicode,则需要使用你希望写入的编码进行encode(),如果是其他编码格式的str,则需要先用该str的编码进行decode(),转成unicode后再使用写入的编码进行encode()。如果直接将unicode作为参数传入write()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入。
模块codecs提供了一个open()方法,可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述操作。
乱码处理方案
在Python进行开发的时候,对字符串统一使用unicode对象来表示,尤其是带中文的字符串,千万不要使用str类型,这样就从根源上避免了Python隐式从str转成unicode的可能性。对于外部传递进来的参数,尤其是网络调用传入的参数,必须先转成unicode类型再进行后续的操作。这种方式比较干净,纯粹。
而在Python3.x版本中,把’xxx’和u’xxx’统一成Unicode编码,即写不写前缀u都是一样的
还有一种方式,就是在Python源码文件的开始处加上下面三行代码
import sys reload(sys) sys.setdefaultencoding("utf-8")
这种方式会改变Python默认从str转成unicode采用的编码方式。只要保证我们的中文字符统一采用UTF-8编码方式,这种方式也能很好解决字符编码问题。但是每个文件总是写上这三行代码看上去会比较dirty。
其他一些获取当前环境下的默认编码的方法
# windows import sys import locale def p(f): print '%s.%s(): %s' % (f.__module__, f.__name__, f()) # 返回当前系统所使用的默认字符编码 p(sys.getdefaultencoding) # sys.getdefaultencoding(): ascii # 返回用于转换Unicode文件名至系统文件名所使用的编码 p(sys.getfilesystemencoding) # sys.getfilesystemencoding(): mbcs # 获取默认的区域设置并返回元祖(语言, 编码) p(locale.getdefaultlocale) #locale.getdefaultlocale(): ('zh_CN', 'cp936') # 返回用户设定的文本数据编码 p(locale.getpreferredencoding) # locale.getpreferredencoding(): cp936 # \xba\xba是'汉'的GBK编码 print r"'\xba\xba'.decode('mbcs'):", repr('\xba\xba'.decode('mbcs')) #'\xba\xba'.decode('mbcs'): u'\u6c49'
一点补充
如果有一个直接的str字符串,如
s = 'id\u003d215903184\u0026index\u003d0\u0026st\u003d52\u0026sid’要装为Unicode,怎么转呢?难道要自己去处理每一个\u吗?
可以使用
s.decode('unicode-escape')
>>> s = 'id\u003d215903184\u0026index\u003d0\u0026st\u003d52\u0026sid\u003d95000\u0026i' >>> print(type(s)) <type 'str'> >>> s = s.decode('unicode-escape') >>> s u'id=215903184&index=0&st=52&sid=95000&i' >>> print(type(s)) <type 'unicode'> >>>
C/C++
C,C++,Python2 内部字符串使用当前系统默认编码,一般情况下当前系统默认编码是GBK(windows),即简体中文编码。C,C++表示字符的类型有char和wchar,char占一个字节,wchar占两个字节,所有编码方式里只有Unicode编码(即UTF-16大端模式和UTF-16小端模式)的编码单元为两字节,故此Unicode编码使用wchar作为字符串的基本单元,对应的字符串类型为wstring。
其它编码方式都使用字节作为基本编码单元,故此都用char作为字符串的基本单元,对应的字符串类型为string。
有两个函数可以用来在wchar类型字符串和char类型字符串之间进行转换:MultiByteToWideChar 和 WideCharToMultiByte。
int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr,int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
codepage取值:
- CP_ACP: 当前系统是Windows ANSI code page。
- CP_MACCP: 当前系统是 mac code page
- CP_UTF8: UTF8编码方式
一些处理方法:
C,C++内部字符串默认使用GBK编码,使用GBK编码的话调试时可识别汉字,故此从外部文件读入字符串之后一般都先转化成GBK编码,这样方便调试。
将字符串写到文件时通常会选择unicode编码,
发送到网络时会选择UTF-8编码。(可以避免Unicode字节序的问题)
Java
Java用String类型表示字符串,String由char类型组成,char类型占两个字节,一个char类型变量就可以表示任意字符,甚至汉字。所以下面代码是合法的:char b='你'; System.out.println((int)b); // 20320 == 0x4f60
在java中unicode编码和utf16大端模式编码是一致的
参考
深入理解Python字符集编码谈谈字符编码的问题
字符编码总结
相关文章推荐
- java的中文字符编码问题-控制台输出
- python学习笔记8-9(字符编码与二进制)
- 2013年5月编程语言排行榜:UNIX下的Bash
- 字符编码之间的转换
- 关于字符编码(挺有用)
- Java IO 4:字符编码
- 多态在 Java 和 C++ 编程语言中的实现比较
- python基础1之python介绍、安装、变量和字符编码、数据类型、输入输出、数据运算、循环
- 字符编码笔记:ASCII,Unicode和UTF-8
- 从这3个渠道了解流行编程语言,涨薪就靠它!
- 【Qt】字符编码、乱码的一点总结
- 2017 年应该学习的编程语言、框架和工具
- 字符编码中ASCII、Unicode和UTF-8的区别
- vim字符编码
- 现在编程语言还重要吗?
- mariadb的python连接,远程连接,字符编码问题总结
- 黑马程序员_IO流——字符编码
- NOI评测环境及对编程语言使用限制的规定
- 各种编程语言这间的关系图
- 字符编码