目录:
Unicode字符集UTF-8编码方案帮助理解 印证上文参考实例【GoPython】正文:
Unicode 是字符集,UTF-32/ UTF-16/ UTF-8 是三种字符编码方案
一定记住上面这句话
Unicode 本身也是一种字符编码方式,一般指:UCS-2 (Unicode Character Set - 用两个字节编码),下文所有『Unicode编码』都指UCS-2编码,注意区分『Unicode字符集』
上面这句选记
Unicode 字符集
在很久以前,世界还是比较简单的,起码计算机世界就只有一个 ASCII 字符集:美国信息交换标准代码。ASCII,更准确地说是美国的 ASCII,使用 7bit 来表示 128 个字符:包含英文字母的大小写、数字、各种标点符号和设备控制符。对于早期的计算机程序来说,这些就足够 了,但是这也导致了世界上很多其它地区的用户无法直接使用自己的符号系统。随着互联网的发展,混合多种语言的数据变得很常见(译注:比如本身的英文原文或中文翻译都包含了 ASCII、中文、日文等多种语言字符)。如何有效处理这些包含了各种语言的丰富多样的文本 数据呢?
答案就是使用 Unicode( Unicode Consortium ),它收集了这个世界上所有的符号系统,包括 重音符号和其它变音符号,制表符和回车符,还有很多神秘的符号,每个符号都分配一个唯 一的 Unicode 码点,Unicode 码点对应 Go 语言中的 rune 整数类型(译注:rune 是 int32 等 价类型)。
这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
Unicode是一个字符集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
UTF-8 编码方案出现了
UTF-8 编码方案
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、游戏问答网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。
举个 ?
下表总结了编码规则,字母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注意下划线标注的1跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
以汉字『严』为例,演示如何实现UTF-8编码,已知『严』的 Unicode 是 4E25(100111000100101),游戏问答根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此『严』的UTF-8编码需要三个字节,即格式是『1110xxxx 10xxxxxx 10xxxxxx』。然后,从『严』的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,『严』的UTF-8编码是『11100100 10111000 10100101』,转换成十六进制就是E4B8A5。
再举个 ?
UTF-8就是以8位为单元对UCS进行编码
下面是UCS-2 - UTF-8 转换规则
UCS-2编码
UTF-8字节流(二进制)
-------------------------------------------------
0000 007F
0xxxxxxx
0080 07FF
110xxxxx 10xxxxxx
0800 FFFF
1110xxxx 10xxxxxx 10xxxxxx
UCS 只是规定如何编码,并没有规定如何传输、保存这个编码,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码
以『汉』字为例,它的Unicode编码是6C49。6C49在0800-FFFF之间,用3字节模板:
1110xxxx 10xxxxxx 10xxxxxx
将6C49写成二进制是:
0110 1100 0100 1001用这个比特流依次代替模板中的x,得到:
11100110 10110001 10001001
即E6 B1 89。
帮助理解 印证:
UTF-8 是一种变长的编码方式,它使用1~4个字节表示一个符号,根据不同的符号而变化字节长度
Unicode字符集 规定了它的二进制代码,并不涉及如何存储这个二进制代码,编码方式则涉及存储
Unicode编码(UCS-2编码)规定了如何编码,并没有规定如何传输,所以一般会先转换成UTF-8等
上文参考:
《Go语言程序设计》
字符编码简介 - 河西无名式 - 博客频道 - CSDN.NETPython 字符编码与解码
字符编码笔记:ASCII,Unicode和UTF-8
谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
稍后我会使用一个或多个编程语言印证部分内容,敬请期待,有错误请不吝赐教,谢谢大家
实例【GoPython】
======== Go 语言 ========
Go 默认提供了很多隐式的操作来处理Unicode,
例如:Go 语言的 range 循环在处理字符串的时候,会自动隐式解码 UTF8 字符串。
看代码吧:
package main import "fmt" import "unicode/utf8" func main() { s := "hello, world" fmt.Println(len(s)) // "12" fmt.Println(s[0], s[7]) // "104 119" 这里的数字是字节,下面的切片也是 fmt.Println(s[0:5]) // "hello" 和Python一样,不包含s[5] fmt.Println(s[:]) // "hello, world" 和Python一样 fmt.Println([]rune(s)) // "[104 101 108 108 111 44 32 119 111 114 108 100]" fmt.Println([]byte(s)) // "[104 101 108 108 111 44 32 119 111 114 108 100]" fmt.Println([]rune("世界")) // "[19990 30028]" fmt.Println([]byte("世界")) // "[228 184 150 231 149 140]" fmt.Println("世界") // "世界" fmt.Println("\xe4\xb8\x96\xe7\x95\x8c") // "世界" fmt.Println("\u4e16\u754c") // "世界" fmt.Println("\U00004e16\U0000754c") // "世界" s1 := "Hello, 世界" fmt.Println(len(s1)) // "13" fmt.Println(utf8.RuneCountInString(s1)) // "9" for i := 0 i len(s1) { r, size := utf8.DecodeRuneInString(s1[i:]) fmt.Printf("%d\t%c\n", i, r) i += size } // 0 H // 1 e // 2 l // 3 l // 4 o // 5 , // 6 // 7 世 // 10 界 for i, r := range s1 { fmt.Printf("%d\t%q\t%d\n", i, r, r) } // 0 H 72 // 1 e 101 // 2 l 108 // 3 l 108 // 4 o 111 // 5 , 44 // 6 32 // 7 世 19990 // 10 界 30028 n := 0 for range s1 { n++ } fmt.Println(n) // "9" s2 := "プログラム" r := []rune(s2) fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]" fmt.Println(string(r)) // "プログラム" fmt.Println(string(65)) // "A" fmt.Println(string(0x4eac)) // "京" fmt.Println(string(1234567)) // "�" 即:\uFFFD } ======== Python 语言 ========
unicode.py
#!/usr/bin/python string=星辰 print string 当执行
python unicode.py的时候会报错误:
File "unicode.py", line 1 SyntaxError: Non-ASCII character \xe6 in file unicode.py on line 1, but no encoding declared see http://python.org/dev/peps/pep-0263/ for details我们看这里:PEP 263 -- Defining Python Source Code Encodings 第一句
Python will default to ASCII as standard encoding if no other encoding hints are given.Python在读取.py文件的时候,会先把 『星辰』保存成 str类型,这个过程需要先编码为Unicode,再解码,在编码的时候,即 `decode()` 那一步,会按默认编码先解码再编码:
str = "星辰" str.encode() # 等价于 str.decode(sys.defaultencoding()).encode()在 decode 的时候就会出错了,因为 `sys.defaultencoding()` 也就是 ascii 无法编码『星辰』这两个中文字符(no encoding declared)
如何解决呢?看官方内容吧:
Python will default to ASCII as standard encoding if no other encoding hints are given.
To define a source code encoding, a magic comment must be placed into the source files either as first or second line in the file, such as:
# coding=encoding name or (using formats recognized by popular editors):
#!/usr/bin/python # -*- coding: encoding name -*- or:
#!/usr/bin/python # vim: set fileencoding=encoding name : More precisely, the first or second line must match the following regular expression:
^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+) The first group of this expression is then interpreted as encoding name. If the encoding is unknown to Python, an error is raised during compilation. There must not be any Python statement on the line that contains the encoding declaration. If the first line matches the second line is ignored.
To aid with platforms such as Windows, which add Unicode BOM marks to the beginning of Unicode files, the UTF-8 signature\xef\xbb\xbf will be interpreted as utf-8 encoding as well (even if no magic encoding comment is given).
If a source file uses both the UTF-8 BOM mark signature and a magic encoding comment, the only allowed encoding for the comment is utf-8. Any other encoding will cause an error.
问题解决了,可是我遇到更费解的问题,我有一个Django 项目,明明所有包含中文的文件都加了:
# coding:utf8但是有些http请求还会有上面的错误,这又是为什么呢?
因为这个只是.py文件的编码指定,如果有网络请求发送数据,那么编码就不一样了,(而requests模块是根据header来编码的,游戏问答好机智)我是这么理解的,至于为什么不一样了,遇到再详看吧,
总之我们一般在项目入口加三行代码搞定,老司机都知道:
import sys sys.reload() sys.setdefaultencoding(utf-8)
一些题外话:
毕竟花了时间边查边写的,就当大家换个角度温习一下,换个思路,总比某 Python专栏各种抄袭强吧??,能给些有建设性的建议我感激不尽【PythonGo】Unicode字符集与UTF8编码(已更新实例代码)!
原文标题:【PythonGo】Unicode字符集与UTF8编码(已更新实例代
|