ascii编码
ASCII可以编码英文字符,0-9数字,以及一些可打印字符,不可打印字符等。
在那个时候,编写程序只使用这个编码就足够,一个字符通过写入后,在内存中保存为其对应的ASCII编号,当从内存中读出时,把对应的ASCII编号转换成对应的字符即可。
UNICODE编码
等到各个国家开始使用计算机后,就有一个问题,如何将本国文字也可以在计算中存储和显示?
ASCII由于最开始只用一个字节后7位,可以表示128个字符,但是世界上的国家那么多,文字又千奇百怪,怎么表示这些所有的字符。这个就产生了unicode编码。unicode把世界上所有的字符进行了编号。因为字符数量很多,所以unicode编码的编号需要用到到2个,3个甚至更多的字节。现在对应的字符编号有了。接下来,如果我们输入一个字符,是不是直接就用这个字符对应的unicode编号进行存储呢?
例如:
汉字严的 Unicode 是十六进制编号4E25,转换成二进制数足足有15位(100111000100101),如果我们直接用这个编号存储在计算机上就会有一个问题,当我们读出来的时候,我们怎么知道他是用两个字节表示一个字符,而不是使用的ascii编码,表示两个字符,也可能表示(‘N%’)。所以我们需要一种编码方式可以使得从内存中读出结果,能知道是两个字节表示一个还是两个字符。这就是utf8编码方式。
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围(十六进制) | UTF-8编码方式(二进制) |
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
这里要说明一下字符集,编码方式的概念。
ASCII既是一种编码集,代表着字符对应的编码,也表示一种编码方式,在计算机中通过ascii编码的方式存储字符。
unicode是一种编码集,代表着字符对应的编码。但是在字符却不是直接把unicode的编码存在计算机上,而是通过utf8这种编码方式存储在计算机上。
下面来看一下python中字符编码
s1是string object,s2 是 unicode object。
>>> s1='严'
>>> s2=u'严'
可以看到在不同对象中,存储的编码是不同的,string object存储的是"严"通过utf8进行编码的形式(因为我是linux系统,默认是utf8编码,如果是windows,可能会是其他编码如GBK等),而unicode object中存储的是"严"对应的unicode编码。
>>> s1
'\xe4\xb8\xa5'
>>> s2
u'\u4e25'
通过print 可以将两种字符都以汉字的形式输出出来。如何做到的,string object通过chardet.detect可以知道s1的编码类型,通过编码类型就可以找到对应的unicode编号,找到对应的汉字。而unicode object直接通过编码就可以找到汉字。
>>> chardet.detect(s1)
{'confidence': 0.505, 'encoding': 'utf-8'}
>>> print s1
严
>>> print s2
严
如果把s1和s2放在list里面,可以输出对应的编码,但是已经无法输出汉字了,这是因为l1和l2已经不是字符串了,print也无法输出汉字
>>> l1=[s1]
>>> l2=[s2]
>>> l1
['\xe4\xb8\xa5']
>>> l2
[u'\u4e25']
>>> print l1
['\xe4\xb8\xa5']
>>> print l2
[u'\u4e25']
如果用json.dumps或者str后呢,可以看到dumps之后list中的字符都变成了unicode编码,即原来用utf8编码记录的list会变为用unicode编码的;而str则不会改变编码方式。print效果一样。
>>> json.dumps(l1)
'["\\u4e25"]'
>>> json.dumps(l2)
'["\\u4e25"]'
>>> str(l1)
"['\\xe4\\xb8\\xa5']"
>>> str(l2)
"[u'\\u4e25']"
>>> print str(l1)
['\xe4\xb8\xa5']
>>> print str(l2)
[u'\u4e25']
>>> print json.dumps(l1)
["\u4e25"]
>>> print json.dumps(l2)
["\u4e25"]
如果要显示中文
>>> print json.dumps(l1, ensure_ascii=False)
["严"]
>>> print json.dumps(l2, ensure_ascii=False)
["严"]
字典也可以使用这种方法显示中文
>>> d1= {'a' : s1}
>>> d2= {'a' : s2}
>>> print d1
{'a': '\xe4\xb8\xa5'}
>>> print d2
{'a': u'\u4e25'}
>>> print json.dumps(d1)
{"a": "\u4e25"}
>>> print json.dumps(d2)
{"a": "\u4e25"}
>>> print json.dumps(d1,ensure_ascii=False)
{"a": "严"}
>>> print json.dumps(d2,ensure_ascii=False)
{"a": "严"}
备注:
有文章说json显示中文需要如下方式:
json.dumps(m,ensure_ascii=False).decode('utf8').encode('gb2312')
这是因为window平台默认的中文编码编码方式是GBK,所以,在使用json.dumps(m,ensure_ascii=False) 时,该函数执行的结果为字符按照utf8方式的编码,print时会把utf8的编码使用GBK进行解码,所以出现乱码。因此先要使用utf8解码,再使用GBK编码,这样在print时,使用默认的GBK解码方式就可以输出正确的中文了。
window平台:
# -*- coding: utf-8 -*-
m = {'a' : '你好'}
print m
=>{'a': '\xe4\xbd\xa0\xe5\xa5\xbd'}
print json.dumps(m)
=>{"a": "\u4f60\u597d"}
print json.dumps(m,ensure_ascii=False)
=>{"a": "浣犲ソ"}
print json.dumps(m,ensure_ascii=False).decode('utf8').encode('gb2312')
=>{"a": "你好"}
ujson与json 模块的对比
使用ujson 输出含有中文的json字符串,分别使用%s和format两种方法。
# -*- coding: utf-8 -*-
import ujson as json
#import json
js_m = '''{"a" : "木马行为 - 告警次数超过阈值", "b":"10.10.10.10"}'''
m = json.loads(js_m)
#print alertDict
#print json.dumps(m)
try:
print "result:'%s' and '%s'" %(json.dumps(m['b']), json.dumps(m, ensure_ascii=False))
except Exception,e:
print e
print type(json.dumps(m, ensure_ascii=False))
print "result:'{}' and '{}'".format(m['b'],json.dumps(m, ensure_ascii=False).encode('utf8'))
print "result:'{}' and '{}'".format(str(m['b']),json.dumps(m, ensure_ascii=False))
print "result:'{}' and '{}'".format(json.dumps(m['b']),json.dumps(m, ensure_ascii=False))
结果:
➜ ~ python utf2.py
result:'"10.10.10.10"' and '{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
<type 'str'>
Traceback (most recent call last):
File "utf2.py", line 16, in <module>
print "result:'{}' and '{}'".format(m['b'],json.dumps(m, ensure_ascii=False).encode('utf8'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 6: ordinal not in range(128)
format输出错误,因为json.dumps(m, ensure_ascii=False)的结果为str,可以使用chardet.detect查看他的编码格式,发现它是utf8的编码,所以这里不能在使用encode(‘utf8’)。
修改为:
# -*- coding: utf-8 -*-
import ujson as json
#import json
js_m = '''{"a" : "木马行为 - 告警次数超过阈值", "b":"10.10.10.10"}'''
m = json.loads(js_m)
#print alertDict
#print json.dumps(m)
try:
print "result:'%s' and '%s'" %(json.dumps(m['b']), json.dumps(m, ensure_ascii=False))
except Exception,e:
print e
print type(json.dumps(m, ensure_ascii=False))
print "result:'{}' and '{}'".format(m['b'],json.dumps(m, ensure_ascii=False))
print "result:'{}' and '{}'".format(str(m['b']),json.dumps(m, ensure_ascii=False))
print "result:'{}' and '{}'".format(json.dumps(m['b']),json.dumps(m, ensure_ascii=False))
结果:
➜ ~ python utf.py
result:'"10.10.10.10"' and '{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
<type 'str'>
{'confidence': 0.99, 'encoding': 'utf-8'}
result:'10.10.10.10' and '{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
result:'10.10.10.10' and '{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
result:'"10.10.10.10"' and '{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
如果%s格式化也用三种方式,会是什么效果:
# -*- coding: utf-8 -*-
import ujson as json
#import json
js_m = '''{"a" : "木马行为 - 告警次数超过阈值", "b":"10.10.10.10"}'''
m = json.loads(js_m)
#print alertDict
#print json.dumps(m)
print type(m['b'])
print type(json.dumps(m, ensure_ascii=False))
print "result:'%s'" %(m['b'])
print "result:'%s'" %((json.dumps(m, ensure_ascii=False)))
print "result:'%s' and '%s'" %(m['b'], unicode(json.dumps(m, ensure_ascii=False)))
print "result:'%s' and '%s'" %(str(m['b']), json.dumps(m, ensure_ascii=False))
print "result:'%s' and '%s'" %(json.dumps(m['b']), json.dumps(m, ensure_ascii=False))
结果:
➜ ~ python utf.py
<type 'unicode'>
<type 'str'>
result:'10.10.10.10'
result:'{"a":"木马行为 - 告警次数超过阈值","b":"10.10.10.10"}'
Traceback (most recent call last):
File "utf.py", line 86, in <module>
print "result:'%s' and '%s'" %(m['b'], json.dumps(m, ensure_ascii=False))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 6: ordinal not in range(128)
原因是:单独输出m[‘b’] 和 json.dumps(m, ensure_ascii=False) 都可以输出,但是一起就不行了。一个是unicode类型,一个是str类,utf8编码。使用%s这种方式无法使用同时输出两种类型的字符。必须同一位一种类型,要么是str,要么是unicode。
两种类型一起输出的例子:
s1=u"sssss"
s2="严"
print "ret:'%s' and '%s'" %(s1, s2)
结果:
➜ ~ python utf.py
Traceback (most recent call last):
File "utf.py", line 92, in <module>
print "ret:'%s' and '%s'" %(s1, s2)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
我们把str类型编程unicode类型就可以了,修改代码:
把
print "result:'%s' and '%s'" %(m['b'], unicode(json.dumps(m, ensure_ascii=False)))
修改为:
print "result:'%s' and '%s'" %(m['b'], unicode(json.dumps(m, ensure_ascii=False),'utf8'))
这样m[‘b’] 和unicode(json.dumps(m, ensure_ascii=False),‘utf8’) 都是 unicode类型了,或者都改为str类型,那就是str(m[‘b’])和json.dumps(m[‘b’])两种方式。
总结
- %s输出方式不能同时格式化输出不同类型的字符,必须同时都变为str类型或者unicode类型。
- foramt 的方式则不存在这样的问题,可以输出两种不同类型的字符。
- ujson 和json的区别是,dumps()函数返回值,ujson是str类型,而json返回的是unicode类型。
- chardet.detect可以检测str类型的编码方式,通过这个函数可以知道str编码类型,更系统平台有关,linux为utf8为主,而window为GBK为主。
- python列表,字典类型的变量输出中文,可以使用json.dumps(x, ensure_ascii=False)。json和ujson一样的。
- 某些情况下要出中文还需要编解码,例如windows平台,默认是GBK编码,使用json.dumps(x, ensure_ascii=False)返回的是unicode类型,需要先用decode(‘utf8’)解码,再用encode(‘GBK’)编码才能正确输出中文字符。
最后再写一个有意思的C程序:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char * argv[]) {
char p[] = {0xe4,0xb8,0xa5};
printf("%s\n", p);
return 0;
}
请问他的输出是什么?
答案是
[yeruoxi@SELKS ~]$ gcc test.c
[yeruoxi@SELKS ~]$ ./a.out
严