字符串编码(utf8)
文章
Things about Unicode everyone needs to know
golang: Strings, bytes, runes and characters in Go
编码发展的历史
早期自由定义的编码集
ASCII码的起源:1.英文字符可用127以内的数字映射,需要7位;2.最初的计算机都是8位的
在ASCII码中,0~32范围的字符称为控制字符,是不可打印的。
接着,不同的制造商就对128~255编码进行自定义。IBM-PC定义了OEM字符集,提供对某些欧洲语言的字符进行表示。随着不同国家的人们开始使用PC,不同的OEM字符集也被定义出来。不同地区的对同一个数字表示的字符具有不同的释义。
最终,这些OEM映射的方案被加入ANSI标准中。在ANSI中,0~127的字符毫无疑问都使统一的ASCII编码,但在128~255的范围内,根据不同的地区采取不同的映射。这些映射被称为code pages。MS-DOS在系统中内置了这些编码映射,如以色列是862,希腊是737。但是,不可能在同一台计算机上同时使用两个编码映射。
同时,在亚洲,字符串的表示更加复杂。因为亚洲的语言通常包含几千个字符,无法使用8位表示。早期使用DBCS(Double Bytes Character Set, 双字节字符集), 有的文字使用单个字节表示,有的使用双字节表示。在这种不定长的编码中,像s++
这样的操作的其实就不再对应字符移动了。但是,大多数人还是按照处理8位字符的方式处理这些字符集,毕竟不需要将这些字符串传送到其他计算机或网络上。但是随着互联网的普及,这些问题逐渐暴露出来。
这是,Unicode出现了。
Unicode
首先要澄清一点,Unicode不是一个"所有字符都是用16位进行编码"的字符集。
在Unicode中,要思考几个问题:
- 不同字体的字符应当使用同一个编码吗?
- 在德语中,ß是一个真正的字符吗?还是仅仅是ss的一种书写方式?
- 如果一个字符的末端发生变化,它们还是同一个字符吗?希伯来文(Hebrew)认为不是,阿拉伯文则认为仍然相同
经过近10年的讨论,Unicode的制定者已经讨论出了上述问题的答案。
在Unicode中,这个星球上的任何一个字符都被映射到一个唯一的数字,表示为:U+0639
,这个数字被称为code point
。U+
表示这是一个Unicode表示。可以使用charmap
(Windows系统)查看所有的字符映射,也可以访问Unicode官网。
Unicode对于所要表示的字符数没有限制,因此并不是所有字符都是16位的。
同一个字符也有多种表达方式:à,作为单个字符时是U+00E0;是可以通过 U+0300 U+0061的组合来产生。U+0300表示音调,U+0061即a.
Unicode的计算机表示:编码
Hello,使用Unicode表示为:U+0048 U+0065 U+006C U+006C U+006F。
这是Unicode的最早编码方式,也是人们认为Unicode使用16位表示一个字符的起源。
在内存中,可以按照大端尾或小端尾分别表示:
大端尾:00 48 00 65 00 6C 00 6C 00 6F
小端尾:48 00 65 00 6C 00 6C 00 6F 00
所以,这就是FE FF
的起源。Unicode文本的开头使用FE FF
来标识字节的顺序,称为Unicode Byte Order Marker(BOM)。通过识别起始的两个字符是FE FF
(大端尾)还是FF FE
(小端尾)来判断文本是大端尾还是小端尾。
使用file
命令测试:
$ echo -ne '\xFE\xFF' > FEFF.txt
$ echo -ne '\xFF\xFE' > FFFE.txt
$ file FFFE.txt FEFF.txt
FFFE.txt: Little-endian UTF-16 Unicode text, with no line terminators
FEFF.txt: Big-endian UTF-16 Unicode text, with no line terminators
由于最初Unicode采用两个字符编码,导致仅英文的文本存储占用空间翻倍。此外,已经存在了大量的ASCII和DBCS编码的文本,Unicode的表示无法兼容。所以一开始,人们都选择无视Unicode。
UTF-8
UTF-8编码解决了兼容性和存储的问题。0~127的字符都是用单个字节表示,只有128以上的字符,才会使用2~6个字节来存储。
单字节: 0vvvvvvv
双字节:110vvvvv 10vvvvvv
3字节:1110vvvv 10vvvvvv 10vvvvvv
4字节:11110vvv 10vvvvvv 10vvvvvv 10vvvvvv
5字节:111110vv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv
6字节:1111110v 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv
注:解释编码时,需要将所有的v组合在一起,得出code point.
此前使用16位编码的格式成为UCS-2
或UTF-16
,对应的还有UCS-4
。UTF-7
则是UTF-8
的一种特化,它保证最高位总是0,因为有些系统认为7位足以表示。
如果一个code point是不可打印的,unicode会打印出?或者�(0xfffd, 65533)。
�的UTF-8编码是 0xef 0xbf 0xbd(3字节编码), 如果使用python -e 'print(ord(“�”))'则会得到65533。
如果使用UTF-8解释一些包含128以上编码的文本,则可能得到一大堆�。
关于字符串最重要的一点:离开编码谈字符串是没有意义的
在计算机中,不存在Plain Text这样的东西。ASCII不意味着Plain Text。
如何表示字符串的编码?在Email中, 通过Header来表示:
Content-Type: text/plain; charset="UTF-8"
网页通常使用Content-Type
来指示文件的编码,但是站在Server的角度考虑,HTML的内容通常来自于文件,如果不同的文件使用不同的编码,就不能简单的使用一个固定的Content-Type
。一般是通过HTML HEADER
来表示:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
注意:meta
必须成为HTML的第一个标签。
如果没有Content-Type,该如何决定文件编码呢?IE使用一个策略:通过统计不同语言编码中各个字节的出现频率,试图找出最匹配的那个编码。
皮斯特法则,鲁棒性原则(出现于TCP协议):对输出持保守态度,对输入持开放态度
代码
程序表示和编码,在python中,运行下面代码:
>>> a='�����'
>>> len(a)
5
>>> a.encode('utf-8')
b'\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd'
>>> len(a.encode('utf-8'))
15
变量a
包含5个unicode字符,但是只有使用encode
才能将其转换成utf-8格式。
在c++中,使用L"Hello"
来表示一个UCS-2编码的字符串,字符的类型是wchar_t
,使用wcs
系列函数替换str
系列函数(wcslen
)。现如今,go默认使用UCS-2编码存储字符串。
golang中字符串
在golang中,字符串实际上只是一组字节。但是因为go的源代码一定是UTF-8编码的,源代码中的字符串字面量在保存时就已经是UTF-8编码的字节了。
使用fmt.Printf
时,实际上是向console写入了一组UTF-8编码的字节流。只有console的编码也是UTF-8时,才能正确显示出字符串。
code point或许显得有些拗口,所以go使用rune
来表示这个概念,rune
和code point是等价的,除此之外,rune
使用32位存储。
range loop:在go中,对字符串进行range循环,会将字符串进行UTF-8解码:
const nihongo = "日本語"
for index, runeValue := range nihongo {fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
}
index还是对应字符串中的起始位置,但是得到的类型却是rune
。
U+65E5 '日' starts at byte position 0
U+672C '本' starts at byte position 3
U+8A9E '語' starts at byte position 6
那么,遍历一个包含非法UTF-8编码的字符串会发生什么呢?
package mainimport ("fmt"
)func main() {var s string = "\xef\x0a"fmt.Print(s)fmt.Print("\nwill loop\n")for i,r := range s {fmt.Printf("%d:%U\n",i,r)}
}
go不会抛出异常,而是将其解释为�。
�will loop
0:U+FFFD
1:U+000A
encoding/utf8
库的使用示例:
const nihongo = "日本語"
for i, w := 0, 0; i < len(nihongo); i += w {runeValue, width := utf8.DecodeRuneInString(nihongo[i:])fmt.Printf("%#U starts at byte position %d\n", runeValue, i)w = width
}
总结:go字符串的性质
- go源代码总是UTF-8编码
- 一个字符串可以包含任何字节
- 一个字符串字面量总是有效的UTF-8序列
- code point被称为
rune
- 字符串不保证正则化
golang正则化: normalization
简而言之,一个字符,比如:à在unicode中有多种表示。正则化的意思是,使同一个字符尽量使用同一种表示。
字符串编码(utf8)相关推荐
- php兼容编码,PHP截取字符串编码(兼容utf-8和gb2312)
昨天晚上和今天上午看了字符编码的问题,还有一些别人截取字符串的函数,自己也写了一个,兼容utf-8和gb2312的 //截取字符串长度.支持utf-8和gb2312编码.若为gb2312,先将其转为u ...
- Java简易转码工具(一个字符串编码是GBK的文本文件,内容转成UTF-8编码)
import java.io.*; import java.nio.charset.StandardCharsets;/*** 当前项目目录下有一个文本文件note.txt,字符串编码* 是GBK的, ...
- js 获取字符串的UTF8编码
蓝牙传递数据就转成字节流就行,即使用getUTF8Bytes()方法就行 // 获取字符串的utf8字节流function getUTF8Bytes(str) {var bytes = [];var ...
- 字符串编码(ASCII, GBK, ANSI, Unicode(‘\u‘), UTF-8编码)
字符串编码的发展 1.首先,计算机只能处理数字,文本转换为数字才能处理.计算机中8个bit作为一个字节,所以一个字节能表示最大的数字就是255 因为计算机是美国人发明的,所以一个字节可以表示所有字符了 ...
- Go 语言重要知识点:字符串、UTF-8 编码、rune
文章目录 字符串 字符串字面值 Unicode rune UTF-8 类型转换 参考<The Go Programming Language>. 字符串 一个字符串是一个不可变的字节序列. ...
- Java 中文字符串编码之GBK转UTF-8
一.乱码的原因 gbk的中文编码是一个汉字用[2]个字节表示,例如汉字"内部"的gbk编码16进制的显示为c4 da b2 bf utf-8的中文编码是一个汉字用[3]个字节表示, ...
- php将字符串转换成utf8编码,php字符串转utf8编码的方法
php字符串转utf8编码的方法 发布时间:2020-09-08 09:47:05 来源:亿速云 阅读:102 作者:小新 这篇文章将为大家详细讲解有关php字符串转utf8编码的方法,小编觉得挺实用 ...
- String字符串编码解码格式
https://blog.csdn.net/qq_35241080/article/details/83001149 //2 如何识别字符串编码 public static String getEnc ...
- python中文字符串编码_浅谈python下含中文字符串正则表达式的编码问题
前言 Python文件默认的编码格式是ascii ,无法识别汉字,因为ascii码中没有中文. 所以py文件中要写中文字符时,一般在开头加 # -*- coding: utf-8 -*- 或者 #co ...
最新文章
- drf-频率组件 权限组件
- jquery实现跨域
- linux控制台界面编程,控制台窗口界面的编程控制(二)
- 目标检测之YOLOv2
- qsort函数应用大全
- EXCEL解析之终极方法WorkbookFactory
- Python+tkinter动态创建与销毁组件小案例
- 系统架构师复习-操作系统
- Skill Level 4 D23
- (转)SQL Server 监控统计阻塞脚本信息
- C++ 引用计数技术及智能指针的简单实现
- 软件工程案例学习-网上购书系统
- 计算机毕业论文基于Python实现的仓库库存管理系统进销存储系统
- 基于Java的办公用品管理系统的设计与实现
- Shopee平台如何实现多店铺管理?虾扑erp实现智能管理!
- openwrt反攻局域网arp攻击shell脚本
- Android中的短信收不到问题,华为的安卓(Android)系统手机收不到短信问题解决方法...
- [翻译]《Programming - Principles and Practice Using C++, Second Edition》- Chapter 1
- 如何听广播来学计算机,MAC使用技巧之苹果itunes如何收听国内的广播?
- Bootstrap实战练习---Web全栈课程体系(表格+巨幕)