firemail
标题: 字符 字符集 字符串 编码 转换 [打印本页]
作者: Qter 时间: 2020-2-13 17:12
标题: 字符 字符集 字符串 编码 转换
本帖最后由 Qter 于 2020-2-17 01:25 编辑
ASCII
ASCII-American Standard Code for Information Interchange
Character repertoire:
ASCII 严格来讲就是7个bit大小的字符集,也就是code point介于0-127之间的字符集合。
7个字节表示的最大数:7bit=0b01111111=0x7f=127 最小数0 共有128个字符 ---标准ASCII 码也叫基础ASCII码
8个字节表示的最大数:8bit=0b11111111=0xff=255 最小数0 共有256个字符 ---扩展ASCII码 LATIN1
Character code:
32-126之间的字元是可打印字元,其他是控制字元。
Character encoding:
ASCII可以没有任何编码就可以在计算机中用一个字节表示,也就是每个code point被表示成等价的单字节二进制形式。
1985年11月 Windows字符集被称作“ANSI字符集”,遵循了ANSI草案和ISO标准(ANSI/ISO8859-1-1987,简“Latin 1”
LATIN1
LATIN1-8bit sing byte coded graphic character sets.
Character repertoire:
Aka ISO-8859-1。是7bit ASCII 字元集的扩充的一种,是8个bit大小的字符集,也就是code point 介于0-255之间的字符集和。
Character code:
包含有191个可打印字符,其余是控制字符或者扩展的欧洲特殊字符。
Character encoding:
与ASCII相似, 每个code point被表示成等价的单字节二进制形式。
UTF8
UTF8-8bit Unicode Transfer Format
Character repertoire:
包含世界上大部分书写系统的使用的字符,大概1百万个code point (1,114,112 = 220 + 216 )。
Character code:
2,684个保留字符。 98893个图形字符。 435个控制,格式化等特殊用途的字符 。
Character encoding:
是针对Unicode的一种变长的字符编码。能表示Unicode标准中任意一个字符。 UTF8可以将一个字符编码乘1到4个字节大小来表示。
1. 128个US-ASCII 字符需要一个字节(U+0000 --- U+007F)
2. 对于带有区分符号的Latin字母和来自西欧的字符需要两个字节(U+0080 --- U+7FF)
3. BMP其他的字符需要3个字节
4. Non-BMP的其他字符需要4个字节。
ASCII, LATIN1, UTF8 关系:
为了向后兼容, Unicode分配128ASCII和256 LATIN1字符的code point没有改变,与它们在ASCII和Latin1的code point 相同。 因此一个只包含ASCII字符的UTF8文件等同于ASCII文件。 同理,每个正确编码的ASCII文件也是有效地UTF8文件。对于12-256的LATIN1字符,因为其编码的特殊性-UTF8需要2个字节来表示一个 LATIN1字符,也就是二者编码的文件当然不能等价。
如果只考虑各个编码单字节的外围:ASCII(0-127), LATIN1(0-255), UTF8(0-253), 一个UTF8编码的字符串也能被保存为LATIN1文件,但显然是乱码的。反过来,LATIN1编码的文件是无法存为一个UTF8的文件,因为一个大于 253的字符在UTF8中是不存在的。
http://mp.weixin.qq.com/s?__biz= ... 5&lang=zh_CN#rd
作者: Qter 时间: 2020-2-13 17:21
本帖最后由 Qter 于 2020-2-14 00:38 编辑
一、QString 转换为 char *
将 QString 转 char *,需要用到 QByteArray 类,QByteArray 类的说明详见 Qt 帮助文档。
因为 char * 最后都有一个'\0'作为结束符,而采用 QString::toLatin1() 时会在字符串后面加上'\0'。
方法如下:
QString str;
char* ch;
QByteArray ba = str.toLatin1(); // must
ch=ba.data();
这样就完成了 QString 向 char * 的转化。经测试程序运行时不会出现 bug。注意第3行,一定要加上,不可以 str.toLatin1().data() 这样一步完成,否则可能会出错。
补充:以上方法当 QString 里不含中文时,没有问题,但是 QString 内含有中文时,转换为 char * 就是乱码,采用如下方法解决:
方法1:
添加GBK编码支持:
#include <QTextCodec>
QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
然后将上面的第3行修改为:
QByteArray ba = str.toLocal8Bit(); // toLocal8Bit 支持中文
方法2:
先将 QString 转为标准库中的 string 类型,然后将 string 转为 char *。如下:
QString filename;
std::string str = filename.toStdString();
const char* ch = str.c_str();
二、 char * 转换为 QString
将 char * 转换为 QString 比较容易操作,我们可以使用 QString 的构造函数进行转换:
QString(const QLatin1String &str);
QLatin1String 的构造函数:
QLatin1String(const char *str);
因此用下面这个语句就可以将 char * ch 转换为 QString str 了,如下:
str = QString(QLatin1String(ch));
QT 读写二进制 (数值)高位在前
https://www.cnblogs.com/findumars/p/4735312.html
作者: Qter 时间: 2020-2-14 00:36
http://www.fmddlmyy.cn/text6.html
谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的动机是两个问题:
问题一:使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?
我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢?
问题二:最近在网上看到一个ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有些糊涂,想不起来UTF-16和UCS2有什么关系。查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。写成一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂,但要求读者知道什么是字节,什么是十六进制。
0、big endian和little endianbig endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。如果将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
1、字符编码、内码,顺带介绍汉字编码字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。
从ASCII、GB2312到GBK,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK都属于双字节字符集 (DBCS)。
2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说,GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字(Unicode码0x3400-0x4db5),一共收录了27484个汉字。
CJK就是中日韩的意思。Unicode为了节省码位,将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 10646-1的中文版,相当于Unicode 1.1。
GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。 例如:UCS的0x3400在GB18030中的编码应该是8139EF30,UCS的0x3401在GB18030中的编码应该是8139EF31。
微软提供了GB18030的升级包,但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体:新宋体-18030,并不改变内码。Windows 的内码仍然是GBK。
这里还有一些细节:
- GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
- 对于任何字符编码,编码单元的顺序是由编码方案指定的,与endian无关。例如GBK的编码单元是字节,用两个字节表示一个汉字。 这两个字节的顺序是固定的,不受CPU字节序的影响。UTF-16的编码单元是word(双字节),word之间的顺序是编码方案指定的,word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。
- GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。
2、Unicode、UCS和UTF前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。
Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。
根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。 在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。
UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。
IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。
2.1、内码和code page目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。
Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。微软也为GB18030定义了code page:CP54936。
3、UCS-2、UCS-4、BMPUCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。
group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
4、UTF编码
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) | UTF-8 字节流(二进制) |
0000 - 007F | 0xxxxxxx |
0080 - 07FF | 110xxxxx 10xxxxxx |
0800 - FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16,可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
5、UTF的字节序和BOMUTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
6、进一步的参考资料我还找了两篇看上去不错的资料,不过因为我开始的疑问都找到了答案,所以就没有看:
我是想清楚所有问题后才开始写这篇文章的,原以为一会儿就能写好。没想到考虑措辞和查证细节花费了很长时间,竟然从下午1:30写到9:00。希望有读者能从中受益。
附录1 再说说区位码、GB2312、内码和代码页有的朋友对文章中这句话还有疑问:
“GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。”
我再详细解释一下:
“GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55区是一级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊”。(这个区位输入法可以自动识别16进制的GB2312和10进制的区位码,也就是说输入B0A1同样会得到“啊”。)
内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。
内码这个词汇,并没有什么官方的定义,代码页也只是微软这个公司的叫法。作为程序员,我们只要知道它们是什么东西,没有必要过多地考证这些名词。
Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢?
是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。
答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。
Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。
有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:
<meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。
再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码,我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根本没提到这一点。
作者: Qter 时间: 2020-2-14 00:37
ANSI:美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE: ANSI)
ANSI (一种字符代码)扩展的ASCII编码 不是上面的缩写
ANSI是一种字符代码,为使计算机支持更多语言,通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码。
为使计算机支持更多语言,通常使用 0x80~0xFFFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。
不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的编码标准。这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
简单的说,在简体中文系统下,ANSI编码代表GB2312编码;在日文操作系统下,ANSI编码代表JS编码。
不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。ANSI编码表示英文字符时用一个字节,表示中文用两个或四个字节。
作者: Qter 时间: 2020-2-15 16:04
ASCII、UTF-8、UTF-16BE、UTF-16LE编码方式的区别 ASCII字符集:8位表示一个字符,这个都不陌生。
下面主要讲UTF-8、UTF-16BE、UTF-16LE之间的区别
这3种编码都属于Unicode编码方式。
UTF-8的特点是对不同范围的字符使用不同长度的编码
Unicode编码(16进制) UTF-8 字节流(二进制)
000000 - 00007F 0xxxxxxx
000080 - 0007FF 110xxxxx 10xxxxxx
000800 - 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。
2. UTF-16BE :16位表示一个字符,以大端方式存放
3. UTF-16LE : 16位表示一个字符,以小端方式存放
大端与小端的区别:以字符 ‘a’ 为例,‘a’ 的编码为[0x61]
UTF-8方式下:[0x61]
UTF-16BE(即大端方式):[0x00 0x61] 大端方式符合人们的习惯
UTF-16LE(即小端方式):[0x61 0x00] 小端方式利于计算机操作
在UCS 编码中有一个叫做 "Zero Width No-Break Space" ,中文译名作“零宽无间断间隔”的字符,又被称作 BOM
打开txt文件,在文件开头就有这个字符标记,如下图所示
文件内容为“abc\n区别"这个字符序列
ASCII、UTF-8、UTF-16BE、UTF-16LE四种编码方式分别如下:
可以看到 UTF-8编码以 EF BB BF 开头
UTF-16BE编码以FE FF 开头
UTF-16LE编码以FF FE 开头
UTF-8编码方式对不同范围的字符用不同长度的位数编码以及大端与小端的区别。
————————————————
版权声明:本文为CSDN博主「ErrorZero」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ErrorZero/article/details/8483344
作者: Qter 时间: 2020-2-15 16:07
UTF-16BE、UTF-16LE、UTF-16 三者之间的区别简介
实际项目开发中,我们有时候可能需要将字符串转换成字节数组,而转化字节数组跟编码格式有关,不同的编码格式转化的字节数组不一样。下面列举了java支持的几种编码格式:
US-ASCII Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set
ISO-8859-1 ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
UTF-8 Eight-bit UCS Transformation Format
UTF-16BE Sixteen-bit UCS Transformation Format, big-endian byte order
UTF-16LE Sixteen-bit UCS Transformation Format, little-endian byte order
UTF-16 Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark
本文重点讲解的是 UTF-16 编码格式字节数组的转化。UTF-16 顾名思义,就是用两个字节表示一个字符。那么用两个字节表示必然存在字节序的问题,即大端小端的问题。下面就来讲讲 UTF-16BE、UTF-16LE、UTF-16 三者之间的区别吧。
UTF-16BE,其后缀是 BE 即 big-endian,大端的意思。大端就是将高位的字节放在低地址表示。
UTF-16LE,其后缀是 LE 即 little-endian,小端的意思。小端就是将高位的字节放在高地址表示。
UTF-16,没有指定后缀,即不知道其是大小端,所以其开始的两个字节表示该字节数组是大端还是小端。即FE FF表示大端,FF FE表示小端。
代码测试
TestUTF_16.java- public static void main(String[] args) {
- String test = "代码";
- try {
- //UTF-16BE编码格式 大端形式编码
- byte[] bytesUTF16BE = test.getBytes("UTF-16BE");
- printHex("UTF-16BE", bytesUTF16BE);
- //UTF-16LE编码格式 小端形式编码
- byte[] bytesUTF16LE = test.getBytes("UTF-16LE");
- printHex("UTF-16LE", bytesUTF16LE);
- //UTF-16
- byte[] bytesUTF16 = test.getBytes("UTF-16");
- printHex("UTF-16", bytesUTF16);
-
- //大端编码格式的字节数组转化
- String strUTF16BE = new String(bytesUTF16BE, "UTF-16BE");
- String strUTF16LE = new String(bytesUTF16BE, "UTF-16LE");
- String strUTF16 = new String(bytesUTF16BE, "UTF-16");
- System.out.println("strUTF16BE:"+strUTF16BE);
- System.out.println("strUTF16LE:"+strUTF16LE);
- System.out.println("strUTF16:"+strUTF16);
-
- //小端编码格式的字节数组转化
- strUTF16BE = new String(bytesUTF16LE, "UTF-16BE");
- strUTF16LE = new String(bytesUTF16LE, "UTF-16LE");
- strUTF16 = new String(bytesUTF16LE, "UTF-16");
- System.out.println("strUTF16BE:"+strUTF16BE);
- System.out.println("strUTF16LE:"+strUTF16LE);
- System.out.println("strUTF16:"+strUTF16);
-
- //自带大小端编码格式的字节数组转化
- strUTF16BE = new String(bytesUTF16, "UTF-16BE");
- strUTF16LE = new String(bytesUTF16, "UTF-16LE");
- strUTF16 = new String(bytesUTF16, "UTF-16");
- System.out.println("strUTF16BE:"+strUTF16BE);
- System.out.println("strUTF16LE:"+strUTF16LE);
- System.out.println("strUTF16:"+strUTF16);
-
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- private static void printHex(String type, byte[] data) {
- StringBuilder builder = new StringBuilder();
- builder.append("type:").append(type).append(": ");
- int temp = 0;
- for (int i = 0; i < data.length; i++) {
- temp = data[i] & 0xFF;
- builder.append("0x").append(Integer.toHexString(temp).toUpperCase()).append(" ");
- }
- System.out.println(builder.toString());
- }
复制代码从上面的测试结果可以看出来,指定大小端编码格式的,转化为字节数组时不会带FE FF或者FF FE。带有FE FF或者FF FE的字节数组可以转化成指定大小端编码格式的字符串。
源码看看String源码中对getBytes的实现
- /**
- * Returns a new byte array containing the characters of this string encoded using the named charset.
- *
- * <p>The behavior when this string cannot be represented in the named charset
- * is unspecified. Use {@link java.nio.charset.CharsetEncoder} for more control.
- *
- * @throws UnsupportedEncodingException if the charset is not supported
- */
- public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
- return getBytes(Charset.forNameUEE(charsetName));
- }
- /**
- * Returns a new byte array containing the characters of this string encoded using the
- * given charset.
- *
- * <p>The behavior when this string cannot be represented in the given charset
- * is to replace malformed input and unmappable characters with the charset's default
- * replacement byte array. Use {@link java.nio.charset.CharsetEncoder} for more control.
- *
- * @since 1.6
- */
- public byte[] getBytes(Charset charset) {
- String canonicalCharsetName = charset.name();
- if (canonicalCharsetName.equals("UTF-8")) {
- return Charsets.toUtf8Bytes(value, offset, count);
- } else if (canonicalCharsetName.equals("ISO-8859-1")) {
- return Charsets.toIsoLatin1Bytes(value, offset, count);
- } else if (canonicalCharsetName.equals("US-ASCII")) {
- return Charsets.toAsciiBytes(value, offset, count);
- } else if (canonicalCharsetName.equals("UTF-16BE")) {
- return Charsets.toBigEndianUtf16Bytes(value, offset, count);
- } else {
- CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
- ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
- byte[] bytes = new byte[buffer.limit()];
- buffer.get(bytes);
- return bytes;
- }
- }
复制代码 https://blog.csdn.net/QQxiaoqiang1573/article/details/84937863
作者: Qter 时间: 2020-2-15 22:52
UTF-16编码详解首先我们来思考UTF-16的设计思路:
我们知道Unicode的范围为0x0~0x10FFFF
首先是BMP区间,也就是0x0~0xFFFF这段区间,正好16位就可以表示,也兼容,两全其美
那么超过BMP区间的怎么办呢?
也就是0xFFFF~0x10FFFF这段,我们先看这段区间有多少个码位,0x10FFFF-0xFFFF=0x100000,那么这个十六进制表示的十进制也就是:1048576个码位
我们既然16位存不下,那肯定就是32位存咯,这个32能理解为什么不?不理解?是因为计算机只能以2的倍数拓展,如果不这么设计,就没办法解析。长短不一,不符合设计思路
32位来存这些数字,那么我们需要怎么存下呢,简单的思考过后,大家认为应该分开存储,也就是将32位分开前16位和后16位,每个16位各存一半
那么每一半存的就是1024(由来:√1048576=1024,也就是1024*1024=1048576),1024代表的是2的10次幂,也就是10位二进制数
这样就知道了,32位二进制数字中,前后16位中各存10位就够用了,但是剩余的6位用来干什么呢?
和UTF-8的设计一样,为了让识别字符串变得容易(从文本的任意位置开始,均能区分一个字符的起始),这里是不是有点儿蒙?
举个栗子:
假设:
0000 0001 代表A
0000 0010 代表B
0000 0001 ,0000 0001 代表 X
0000 0010 ,0000 0001 代表Z
那么 ABXZ就是
0000 0001 ,0000 0010 , 0000 0001 ,0000 0001 , 0000 0010,0000 0001
A B X Z
但是让你从中间开始读取,当你读到X的时候,你不知道他是X还是 AB,这样就很麻烦,你需要设置标志,来让16位的数据的前8或后8不会和单个8位的重复
可以这样设计:
0xxx xxxx 代表0~2^7
11xx xxxx ,10xx xxxx 代表其他的
这样就能区分开了,当你读到11开头的,就代表他是16位的前8,10开头代表16位的后8
欧了,有了这个思路,我们就知道怎么设计刚才的那个6位了,当然是通过这6位来区分这16位数字代表的位置
也就是UTF-16中,表示数据有单16位和双16位(32位)两种,那么我们设计成单16位和32位中的前16位和后16位这三个16位完全不会重复,那么我们就能随时读到一组16位,就能知道他是单16还是前16还是后16
举个栗子:
根据上方信息,要求我们通过前6位来区分数据,那么前6位就是2^6=64,也就是开头数字的区间
我们设定如下:
54开头的为32位的前16位
55开头的为32位的后16位
其他开头的为单16位
这样我们就能区分开这三个16位了,在读取文档中的任意位置,都能随意区分出间隔咯
那么54开头的数据区间是多少呢,就是1101 10xx xxxx xxxx,区间就是D800~DBFF
那么55开头的数据区间是多少呢,就是1101 11xx xxxx xxxx,区间就是DC00~DFFF
为了配合UTF-16,Unicode中也将这两个区间屏蔽掉,不允许分配任何字符
下方为比较官方的关于UTF-16的编码详解
参考文献:
https://en.wikibooks.org/wiki/Un ... reference/D000-DFFF
根据参考文献1中所示,D800~DFFF为专门提供给UTF-16专用,原文如下:
Unicode range D800–DFFF is used for surrogate pairs in UTF-16 (used by Windows) and CESU-8 transformation formats,
Unicode范围D800-DFFF用于UTF-16(由Windows使用)和CESU-8转换格式的代理对,
allowing these encodings to represent the supplementary plane code points, whose values are too large to fit in 16 bits.
允许这些编码表示辅助平面代码点,其值太大,无法容纳16位。
A pair of 16-bit code points — the first from the high surrogate area (D800–DBFF),and the second from the low surrogate area (DC00–DFFF) — are combined to form a 32-bit code point from the supplementary planes.
一对16位代码点 - 第一个来自高代理区域(D800-DBFF),和来自低代理区域(DC00-DFFF)的第二个组合以从辅助平面形成32位代码点。
Unicode and ISO/IEC 10646 do not assign actual characters to any of the code points in the D800–DFFF range — these code points only have meaning when usedin surrogate pairs.
Unicode和ISO / IEC 10646不向D800-DFFF范围中的任何代码点分配实际字符 - 这些代码点仅在使用时才有意义在替代对。
Hence an individual code point from a surrogate pair does not represent a character, is invalid unless used in a surrogate pair, and is unconditionally invalid in UTF-32 and UTF-8 (if strict conformance to the standard is applied).
因此,来自代理对的单个代码点不表示字符,除非在代理对中使用,否则是无效的,并且是无条件无效的UTF-32和UTF-8(如果严格遵守标准)。
字符按照UTF-16进行编码的规则是: - 字符的值小于0x10000的用等于该值的16位整数来表示。 - 字符的值介于0x10000和0x10FFFF之间的,用一个值介于0xD800和0xDBFF(在所谓的高8位区)的16位整数和值介于0xDC00和0xDFFF(在所谓的低8位区)的16位整数来表示。 - 字符的值大于0x10FFFF不能按照UTF-16进行编码。注意:在0xD800和0xDFFF间的值是特别为UTF-16预留,所以不应该将任何字符的值指定为这个区间内的数值。
D800-DB7F High Surrogates 高位替代 895
DB80-DBFF High Private Use Surrogates 高位专用替代 127
DC00-DFFF Low Surrogates 低位替代 1023
高位替代就是指这个范围的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORD的UTF-16编码的第二个WORD。那么,高位专用替代是什么意思?我们来解答这个问题,顺便看看怎么由UTF-16编码推导Unicode编码。
如果一个字符的UTF-16编码的第一个WORD在0xDB80到0xDBFF之间,那么它的Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是0xDB80 0xDC00到0xDBFF 0xDFFF。我们将这个范围写成二进制:
1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111
按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到
1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111
即0xe0000-0xfffff,按照编码的相反步骤再加上0x10000,得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD在0xdb80到0xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB80到0xDBFF之间的保留码位被称作高位专用替代。
————————————————
版权声明:本文为CSDN博主「Lobxxx」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xinbaobaoer/article/details/56290210
作者: Qter 时间: 2020-2-15 22:53
Unicode
https://baike.baidu.com/item/UniCode/750500
起源[url=]编辑[/url]
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),0 - 255被用来表示大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。
如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。
类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。
因为Python的诞生比Unicode标准发布的时间还要早,所以最早的Python只支持ASCII编码,普通的字符串'ABC'在Python内部都是ASCII编码的。
Unicode 是为了解决传统的字符编码方案的局限而产生的,例如ISO 8859所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不兼容的情况。很多传统的编码方式都有一个共同的问题,即容许电脑处理双语环境(通常使用拉丁字母以及其本地语言),但却无法同时支持多语言环境(指可同时处理多种语言混合的情况)。
Unicode 编码包含了不同写法的字,如“ɑ/a”、“户/户/戸”。然而在汉字方面引起了一字多形的认定争议。
在文字处理方面,统一码为每一个字符而非字形定义唯一的代码(即一个整数)。换句话说,统一码以一种抽象的方式(即数字)来处理字符,并将视觉上的演绎工作(例如字体大小、外观形状、字体形态、文体等)留给其他软件来处理,例如网页浏览器或是文字处理器。
几乎所有电脑系统都支持基本拉丁字母,并各自支持不同的其他编码方式。Unicode为了和它们相互兼容,其首256字符保留给ISO 8859-1所定义的字符,使既有的西欧语系文字的转换不需特别考量;并且把大量相同的字符重复编到不同的字符码中去,使得旧有纷杂的编码方式得以和Unicode编码间互相直接转换,而不会丢失任何信息。举例来说,全角格式区段包含了主要的拉丁字母的全角格式,在中文、日文、以及韩文字形当中,这些字符以全角的方式来呈现,而不以常见的半角形式显示,这对竖排文字和等宽排列文字有重要作用。
在表示一个Unicode的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在基本多文种平面(英文为 Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要用四位十六进制数(例如U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五位或六位十六进制数了。旧版的Unicode标准使用相近的标记方法,但却有些微的差异:在Unicode 3.0里使用“U-”然后紧接着八位数,而“U+”则必须随后紧接着四位数。
作用[url=]编辑[/url]
能够使计算机实现跨语言、跨平台的文本转换及处理。
层次[url=]编辑[/url]
Unicode 编码系统,可分为编码方式和实现方式两个层次。
方式[url=]编辑[/url]
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。
通用字符集(Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4用4个字节编码。
历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟。前者开发的 ISO/IEC 10646 项目,后者开发的统一码项目。因此最初制定了不同的标准。
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都存在,并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode一般都会采用有关字码最常见的字型,但ISO 10646一般都尽可能采用Century字型。
UCS-4根据最高位为0的最高字节分成27=128个组(group)。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行 (row),每行有256个码位(cell)。group 0的平面0被称作BMP(Basic Multilingual Plane)。如果UCS-4的前两个字节为全零,那么将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。每个平面有216=65536个码位。Unicode计划使用了17个平面,一共有17×65536=1114112个码位。在Unicode 5.0.0版本中,已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD和0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为PUA。
平面0也有一个专用区:0xE000-0xF8FF,有6400个码位。平面0的0xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域。代理区的目的用两个UTF-16字符表示BMP以外的字符。在介绍UTF-16编码时会介绍。
如前所述在Unicode 5.0.0版本中,238605-65534*2-6400-2048=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上,它们对应着Unicode定义的99089个字符,其中包括71226个汉字。平面0、平面1、平面2和平面14上分别定义了52080、3419、43253和337个字符。平面2的43253个字符都是汉字。平面0上定义了27973个汉字。
在Unicode中:汉字“字”对应的数字是23383(十进制),十六进制表示为5B57。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。
例如,“汉字”对应的数字是0x6c49和0x5b57,而编码的程序数据是:
[size=1em][size=1em]1
[size=1em]2
[size=1em]3
| [size=1em][size=1em]char data_utf8[]={0xE6,0xB1,0x89,0xE5,0xAD,0x97};//UTF-8编码
[size=1em]char16_t data_utf16[]={0x6C49,0x5B57}; //UTF-16编码
[size=1em]char32_t data_utf32[]={0x00006C49,0x00005B57};//UTF-32编码
|
这里用char、char16_t、char32_t分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以char、char16_t、char32_t作为编码单位。(注: char16_t 和 char32_t 是 C++ 11 标准新增的关键字。如果你的编译器不支持 C++ 11 标准,请改用 unsigned short 和 unsigned long。)“汉字”的UTF-8编码需要3个字节。“汉字”的UTF-16编码需要两个char16_t,大小是2个字节。“汉字”的UTF-32编码需要两个char32_t,大小是4个字节。根据字节序的不同,UTF-16可以被实现为UTF-16LE或UTF-16BE,UTF-32可以被实现为UTF-32LE或UTF-32BE。下面介绍UTF-8、UTF-16、UTF-32、字节序和BOM。
UTF-8
UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
Unicode编码(十六进制)
| UTF-8 字节流(二进制)
|
000000-00007F
| 0xxxxxxx
|
000080-0007FF
| 110xxxxx 10xxxxxx
|
000800-00FFFF
| 1110xxxx 10xxxxxx 10xxxxxx
|
010000-10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。
例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
例2:Unicode编码0x20C30在0x010000-0x10FFFF之间,使用4字节模板:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
UTF-16
UTF-16编码以16位无符号整数为单位。我们把Unicodeunicode
编码记作U。编码规则如下:
如果U<0x10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。
如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。
为什么U'可以被写成20个二进制位?Unicode的最大码位是0x10FFFF,减去0x10000后,U'的最大值是0xFFFFF,所以肯定可以用20个二进制位表示。例如:Unicode编码0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。
按照上述规则,Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。
为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate):
D800-DB7F
| High Surrogates
| 高位替代
|
DB80-DBFF
| High Private Use Surrogates
| 高位专用替代
|
DC00-DFFF
| Low Surrogates
| 低位替代
|
高位替代就是指这个范围的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORD的UTF-16编码的第二个WORD。那么,高位专用替代是什么意思?我们来解答这个问题,顺便看看怎么由UTF-16编码推导Unicode编码。
如果一个字符的UTF-16编码的第一个WORD在0xDB80到0xDBFF之间,那么它的Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是0xDB80 0xDC00到0xDBFF 0xDFFF。我们将这个范围写成二进制:
1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111
按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到
1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111XML
即0xe0000-0xfffff,按照编码的相反步骤再加上0x10000,得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD在0xdb80到0xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB80到0xDBFF之间的保留码位被称作高位专用替代。
UTF-32
UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。
字节序
字节序有两种,分别是“大端”(Big Endian, BE)和“小端”(Little Endian, LE)。
根据字节序的不同,UTF-16可被实现为UTF-16LE或UTF-16BE,UTF-32可被实现为UTF-32LE或UTF-32BE。例如:
Unicode编码
| UTF-16LE
| UTF-16BE
| UTF32-LE
| UTF32-BE
|
0x006C49
| 49 6C
| 6C 49
| 49 6C 00 00
| 00 00 6C 49
|
0x020C30
| 30 DC 43 D8
| D8 43 DC 30
| 30 0C 02 00
| 00 02 0C 30
|
Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符“零宽无中断空格”。这个字符的编码是FEFF,而反过来的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。
下表是各种UTF编码的BOM:
UTF编码
| Byte Order Mark (BOM)
|
UTF-8 without BOM | 无 |
UTF-8 with BOM
| EF BB BF
|
UTF-16LE
| FF FE
|
UTF-16BE
| FE FF
|
UTF-32LE
| FF FE 00 00
|
UTF-32BE
| 00 00 FE FF
|
作者: Qter 时间: 2020-2-16 12:26
细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4
1. Unicode与ISO 10646
全世界很多个国家都在为自己的文字编码,并且互不想通,不同的语言字符编码值相同却代表不同的符号(例如:韩文编码EUC-KR中“한국어”的编码值正好是汉字编码GBK中的“茄惫绢”)。因此,同一份文档,拷贝至不同语言的机器,就可能成了乱码,于是人们就想:我们能不能定义一个超大的字符集,它可以容纳全世界所有的文字字符,再对它们统一进行编码,让每一个字符都对应一个不同的编码值,从而就不会再有乱码了。
如果说“各个国家都在为自己文字独立编码”是百家争鸣,那么“建立世界统一的字符编码”则是一统江湖,谁都想来做这个武林盟主。早前就有两个机构试图来做这个事:
(1) 国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。
(2) 统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard,这个前缀Uni很牛逼哦---Unique, Universal, and Uniform)。
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。不过由于Unicode这一名字比较好记,因而它使用更为广泛。
Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(code point)。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。
2. UTF-32与UCS-4
在Unicode与ISO 10646合并之前,ISO 10646标准为“通用字符集”(UCS)定义了一种31位的编码形式(即UCS-4),其编码固定占用4个字节,编码空间为0x00000000~0x7FFFFFFF(可以编码20多亿个字符)。
UCS-4有20多亿个编码空间,但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。由此UTF-32编码被提出来了,它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集。
3. UTF-16与UCS-2
除了UCS-4,ISO 10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),其编码固定占用2个字节,它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)。例:“汉”的UCS-2编码为6C49。
但俩个字节并不足以正真地“一统江湖”(a fixed-width 2-byte encoding could not encode enough characters to be truly universal),于是UTF-16诞生了,与UCS-2一样,它使用两个字节为全世界最常用的63K字符编码,不同的是,它使用4个字节对不常用的字符进行编码。UTF-16属于变长编码。
前面提到过:Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(code point),而第一个平面称为“基本多语言平面”(Basic Multilingual Plane,简称BMP),其余平面称为“辅助平面”(Supplementary Planes)。其中“基本多语言平面”(0~0xFFFF)中0xD800~0xDFFF之间的码位作为保留,未使用。UCS-2只能编码“基本多语言平面”中的字符,此时UTF-16与UCS-2的编码一样(都直接使用Unicode的码位作为编码值),例:“汉”在Unicode中的码位为6C49,而在UTF-16编码也为6C49。另外,UTF-16还可以利用保留下来的0xD800-0xDFFF区段的码位来对“辅助平面”的字符的码位进行编码,因此UTF-16可以为Unicode中所有的字符编码。
UTF-16中如何对“辅助平面”进行编码呢?
Unicode的码位区间为0~0x10FFFF,除“基本多语言平面”外,还剩0xFFFFF个码位(并且其值都大于或等于0x10000)。对于“辅助平面”内的字符来说,如果用它们在Unicode中码位值减去0x10000,则可以得到一个0~0xFFFFF的区间(该区间中的任意值都可以用一个20-bits的数字表示)。该数字的前10位(bits)加上0xD800,就得到UTF-16四字节编码中的前两个字节;该数字的后10位(bits)加上0xDC00,就得到UTF-16四字节编码中的后两个字节。例如:
(这个字念啥?^_^)
上面这个汉字的Unicode码位值为2AEAB,减去0x10000得到1AEAB(二进制值为0001 1010 1110 1010 1011),前10位加上D800得到D86B,后10位加上DC00得到DEAB。于是该字的UTF-16编码值为D86BDEAB(该值为大端表示,小端为6BD8ABDE)。 4. UTF-8
从前述内容可以看出:无论是UTF-16/32还是UCS-2/4,一个字符都需要多个字节来编码,这对那些英语国家来说多浪费带宽啊!(尤其在网速本来就不快的那个年代。。。)由此,UTF-8产生了。在UTF-8编码中,ASCII码中的字符还是ASCII码的值,只需要一个字节表示,其余的字符需要2字节、3字节或4字节来表示。
UTF-8的编码规则:
(1) 对于ASCII码中的符号,使用单字节编码,其编码值与ASCII值相同(详见:U0000.pdf)。其中ASCII值的范围为0~0x7F,所有编码的二进制值中第一位为0(这个正好可以用来区分单字节编码和多字节编码)。 (2) 其它字符用多个字节来编码(假设用N个字节),多字节编码需满足:第一个字节的前N位都为1,第N+1位为0,后面N-1 个字节的前两位都为10,这N个字节中其余位全部用来存储Unicode中的码位值。
字节数 | Unicode | UTF-8编码 |
1 | 000000-00007F | 0xxxxxxx |
2 | 000080-0007FF | 110xxxxx 10xxxxxx |
3 | 000800-00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 010000-10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
5. 总结
(1) 简单地说:Unicode属于字符集,不属于编码,UTF-8、UTF-16等是针对Unicode字符集的编码。
(2) UTF-8、UTF-16、UTF-32、UCS-2、UCS-4对比:
对比 | UTF-8 | UTF-16 | UTF-32 | UCS-2 | UCS-4 |
编码空间 | 0-10FFFF | 0-10FFFF | 0-10FFFF | 0-FFFF | 0-7FFFFFFF |
最少编码字节数 | 1 | 2 | 4 | 2 | 4 |
最多编码字节数 | 4 | 4 | 4 | 2 | 4 |
是否依赖字节序 | 否 | 是 | 是 | 是 | 是 |
参考:
作者: Qter 时间: 2020-2-16 15:28
https://www.cnblogs.com/huluwa508/p/10827284.html
ASCII、Unicode、UTF-8、UTF-8(without BOM)、UTF-16、UTF-32傻傻分不清
目录
ASCII、Unicode、UTF-8、UTF-8(without BOM)、UTF-16、UTF-32傻傻分不清前言Github上下载了一份代码打算学习,源工程是在linux上开发的,我在Windows上编译通过不了,很多莫名奇妙的错误,最后发现源代码文件是UTF-8(without BOM)编码的,Notepad++修改编码格式为UTF-8编译通过。
- 为什么Windows不认识UTF-8(without BOM)?
- 为什么Linux认识UTF-8(without BOM)和UTF-8?
ASCII毕竟在电子系混过四年,这个词不陌生,用一个字节的低7位来表示128个英文字符(0xxxxxxx),可是地球上的文字又不是只有英文,光汉字就好几万个,所以每个国家和地区又做了一套符合自身情况的编码规范,比如简体中文编码标准GB2312,使用两个字节来表示一个汉字,可以表示65536个中文字符。但是如果每个国家都这么搞那不就乱套了嘛,于是Unicode就应运而生了。
UnicodeUnicode是个符号集,与ASCII类似,只不过容量要大得多,可以理解成一张表,为世界上的每一个字符指定了一个惟一的二进制代码,但是它并没有规定这个二进制代码如何存储,于是乎UTF-8、UTF-8(without bom)、UTF-16、UTF-32应运而生。
UTFUTF(Unicode Transformation Format)意为把Unicode字符转换成某种格式,常见到的有:
UTF-8:使用1至4个字节为每个字符进行编码,节省空间。
UTF-16:使2或4个字节为每个字符编码,大多数汉字采用2个字节,少了生僻字使用4个字节,编码单元为2个字节,所以存在字节序的问题,即大端还是小端。(不常用)
UTF-32:使4个字节为每个字符编码,编码单元为4个字节,所以存在字节序的问题,即大端还是小端。(不常用)
UTF-8UTF-8是Unicode的实现方式之一,最大特点就是根据符号自动变化字节长度,即可变长编码,编码方式如下图所示:
Unicode符号范围 UTF-8编码 (十六进制) (二进制)————————————————————————————————————————————————————————————0000 0000 0000 007F | 0xxxxxxx0000 0080 0000 07FF | 110xxxxx 10xxxxxx0000 0800 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 0000 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxUTF-8(without BOM)BOM(Byte Order Mark)字节顺序标记,即可以用来标记是大端还是小端。在Unicode里面定义了一个叫做
”ZERO WITH NO-BREAK SPACE“的不可见字符,对应的Unicode编码是FEFF,有BOM的文件即文件开头有”ZERO WITH NO-BREAK SPACE“不可见字符,反之则没有。若是大端编码,则文件开头是FEFF,小端则是FFFE。BOM是为了配合UTF-16和UTF-32使用,因为它们编码编码单元包含多个字节,涉及字节序的问题。
UTF-8以单字节为编码单元,不存在字节序的问题,但是可以使用BOM来表明所使用的编码方式,字符”ZERO WITH NO-BREAK SPACE“在UTF-8中的编码是EF BB BF,所以当解码文件时发现开头的单个字节是EF BB BF即说明是UTF-8编码,Windows就是使用BOM来标记文本的编码方式的。
怎样区分UTF-8、UTF-16和UTF-32打开文本时根据BOM来区分当前文件的编码类型
BOM 编码类型——————————————————————————————————————EF BB BF UTF-8FE FF UTF-16(大端)FF FE UTF-16(小端)00 00 FE FF UTF-32(大端)FF FE 00 00 UTF-32(小端)
作者: Qter 时间: 2020-2-16 17:08
字库内码,字库的编码平台字库内码
字库都会设置内码以便于程序调用字库的文字,通用字库中一般采用 Unicode 编码标准,微软平台字库中通常选择“microsoft unicode BMP only”编码系统,意为“微软平台的只在基本多文种平面的 Unicode 编码”,BMP 是 Basic Multilingual Plane 的简写,称为基本多文种平面,或是 0 号平面(Plane 0),是 Unicode 中的一个编码区段,编码从 U+0000 至 U+FFFF。这个编码范围限定每个字符占用两个字节,一共最多可以表示 65536 个字符。 说到 Unicode 必须同时说一说 UCS-2 和 UCS-4
早期 Unicode 在编制通用字符集之时,ISO 组织也在做同样的事情,ISO 开展了 ISO/IEC 10646 项目,名字叫“ Universal Multiple-Octet Coded Character Set”,中文译为“通用多八位编码字符集”,英文简称UCS。
后来,双方意识到不需要同时制定两套通用的字符集,所以双方开始进行整合,到 Unicode 2.0 时,Unicode 编码和 UCS 编码都基本一致。
UCS-2 采用 16 位存储空间,两个字节编码每个字符,而 UCS-4 采用 4 个字节(实际上只用了 31 位,最高位必须为 0)编码。UCS-2 有 216=65536 个码位,UCS-4 有 231=2147483648 个码位。
UCS-4 根据最高位为 0 的最高字节分成 27=128 个组(group)。每个组再根据次高字节分为 256 个平面(plane)。每个平面根据第 3 个字节分为 256 行 (rows),每行包含 256 个单元(cells)。当然同一行的单元只是最后一个字节不同,其余都相同。
0 组的 0 号平面被称作 Basic Multilingual Plane,即基本多文种平面,简写 BMP。可知 BMP 区域内的字符只使用了两个字节,码位从 U+0000 至 U+FFFF。它实际上就是 UCS-2 的全部编码范围,后来因为码位不够用才扩展为 UCS-4。
Unicode 最初也是采用两个字节编码字符,后来发现不够用才扩展为四个字节(与 UCS-4 对应)。理论上 UCS-4 编码范围能达到 U+7FFFFFFF,容纳二十多亿个字符,但是因为 Unicode 和 ISO 达成共识,只会用到 17 个平面,包含 1 个基本平面(BMP)和 16 个辅助平面,最高码位 U+10FFFF。
平面 | 编码范围 | 中文名称 | 英文名称 |
0号平面 | U+0000 - U+FFFF | 基本多文种平面 | Basic Multilingual Plane,简称BMP |
1号平面 | U+10000 - U+1FFFF | 多文种补充平面 | Supplementary Multilingual Plane,简称SMP |
2号平面 | U+20000 - U+2FFFF | 表意文字补充平面 | Supplementary Ideographic Plane,简称SIP |
3号平面 | U+30000 - U+3FFFF | 表意文字第三平面(未正式使用) | Tertiary Ideographic Plane,简称TIP |
4~13号平面 | U+40000 - U+DFFFF | (尚未使用) | |
14号平面 | U+E0000 - U+EFFFF | 特别用途补充平面 | Supplementary Special-purpose Plane,简称SSP |
15号平面 | U+F0000 - U+FFFFF | 保留作为私人使用区(A区) | Private Use Area-A,简称PUA-A |
16号平面 | U+100000 - U+10FFFF | 保留作为私人使用区(B区) | Private Use Area-B,简称PUA-B |
字库中的编码平台
字库中都要指定编码平台,用 FontCreator 打开字库文件,通常你会看到这些选项:
Macintosh Roman
Microsoft Unicode BMP only [ 新版对应: Windows Unicode BMP (UCS-2) ]
Microsoft Unicode full repertoire [新版对应: Windows Unicode UCS-4 ]
Macintosh Roman 是苹果系统用的;下面两项是微软 Windows 系统用的。其中 BMP only 意指编码范围仅限基本多文种平面(Basic Multilingual Plane),最多容纳 65536 个字符;full repertoire 意指能容纳全部 Unicode 字符。
https://www.qqxiuzi.cn/wz/zixun/1663.htm
作者: Qter 时间: 2020-2-16 18:01
字符编码笔记:ASCII,Unicode 和 UTF-8http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
[size=1.6em]今天中午,我突然想搞清楚 Unicode 和 UTF-8 之间的关系,就开始查资料。
[size=1.6em]这个问题比我想象的复杂,午饭后一直看到晚上9点,才算初步搞清楚。
[size=1.6em]下面就是我的笔记,主要用来整理自己的思路。我尽量写得通俗易懂,希望能对其他朋友有用。毕竟,字符编码是计算机技术的基石,想要熟练使用计算机,就必须懂得一点字符编码的知识。
一、ASCII 码[size=1.6em]我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。
[size=1.6em]上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。
[size=1.6em]ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。
二、非 ASCII 编码[size=1.6em]英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。
[size=1.6em]但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。
[size=1.6em]至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。
[size=1.6em]中文编码的问题需要专文讨论,这篇笔记不涉及。这里只指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的 Unicode 和 UTF-8 是毫无关系的。
三. Unicode[size=1.6em]正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。
[size=1.6em]可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码。
[size=1.6em]Unicode 当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字严。具体的符号对应表,可以查询
unicode.org,或者专门的
汉字对应表。
四、Unicode 的问题[size=1.6em]需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
[size=1.6em]比如,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
[size=1.6em]这里就有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
[size=1.6em]它们造成的结果是:1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode。2)Unicode 在很长一段时间内无法推广,直到互联网的出现。
五、UTF-8[size=1.6em]互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。
[size=1.6em]UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
[size=1.6em]UTF-8 的编码规则很简单,只有二条:
[size=1.6em]1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
[size=1.6em]2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
[size=1.6em]下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围 | UTF-8编码方式(十六进制) | (二进制)----------------------+---------------------------------------------0000 0000-0000 007F | 0xxxxxxx0000 0080-0000 07FF | 110xxxxx 10xxxxxx0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
[size=1.6em]跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
[size=1.6em]下面,还是以汉字严为例,演示如何实现 UTF-8 编码。
[size=1.6em]严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。
六、Unicode 与 UTF-8 之间的转换[size=1.6em]通过上一节的例子,可以看到严的 Unicode码 是4E25,UTF-8 编码是E4B8A5,两者是不一样的。它们之间的转换可以通过程序实现。
[size=1.6em]Windows平台,有一个最简单的转化方法,就是使用内置的记事本小程序notepad.exe。打开文件后,点击文件菜单中的另存为命令,会跳出一个对话框,在最底部有一个编码的下拉条。
[size=1.6em]
[size=1.6em]里面有四个选项:ANSI,Unicode,Unicode big endian和UTF-8。
[size=1.6em]1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对 Windows 简体中文版,如果是繁体中文版会采用 Big5 码)。
[size=1.6em]2)Unicode编码这里指的是notepad.exe使用的 UCS-2 编码方式,即直接用两个字节存入字符的 Unicode 码,这个选项用的 little endian 格式。
[size=1.6em]3)Unicode big endian编码与上一个选项相对应。我在下一节会解释 little endian 和 big endian 的涵义。
[size=1.6em]4)UTF-8编码,也就是上一节谈到的编码方法。
[size=1.6em]选择完"编码方式"后,点击"保存"按钮,文件的编码方式就立刻转换好了。
七、Little endian 和 Big endian[size=1.6em]上一节已经提到,UCS-2 格式可以存储 Unicode 码(码点不超过0xFFFF)。以汉字严为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,这就是 Big endian 方式;25在前,4E在后,这是 Little endian 方式。
[size=1.6em]这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-endian)敲开还是从小头(Little-endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
[size=1.6em]第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。
[size=1.6em]那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
[size=1.6em]Unicode 规范定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(zero width no-break space),用FEFF表示。这正好是两个字节,而且FF比FE大1。
[size=1.6em]如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
八、实例[size=1.6em]下面,举一个实例。
[size=1.6em]打开"记事本"程序notepad.exe,新建一个文本文件,内容就是一个严字,依次采用ANSI,Unicode,Unicode big endian和UTF-8编码方式保存。
[size=1.6em]然后,用文本编辑软件
UltraEdit 中的"十六进制功能",观察该文件的内部编码方式。
[size=1.6em]1)ANSI:文件的编码就是两个字节D1 CF,这正是严的 GB2312 编码,这也暗示 GB2312 是采用大头方式存储的。
[size=1.6em]2)Unicode:编码是四个字节FF FE 25 4E,其中FF FE表明是小头方式存储,真正的编码是4E25。
[size=1.6em]3)Unicode big endian:编码是四个字节FE FF 4E 25,其中FE FF表明是大头方式存储。
[size=1.6em]4)UTF-8:编码是六个字节EF BB BF E4 B8 A5,前三个字节EF BB BF表示这是UTF-8编码,后三个E4B8A5就是严的具体编码,它的存储顺序与编码顺序是一致的。
九、延伸阅读[size=1.6em](完)
作者: Qter 时间: 2020-2-16 18:39
UCS-2和UCS-4https://blog.csdn.net/imxiangzi/article/details/77371228
http://guoxinmiao8.blog.sohu.com/129816401.html
介绍Unicode之前,首先要讲解一些基础知识。虽然跟Unicode没有直接的关系,但想弄明白Unicode,没这些还真不行。
字节和字符的区别咦,字节和字符能有什么区别啊?不都是一样的吗?完全正确,但只是在古老的DOS时代。当Unicode出现后,字节和字符就不一样了。
字节(octet)是一个八位的存储单元,取值范围一定是0~255。而字符(character,或者word)为语言意义上的符号,范围就不一定了。例如在UCS-2中定义的字符范围为0~65535,它的一个字符占用两个字节。
Big Endian和Little Endian上面提到了一个字符可能占用多个字节,那么这多个字节在计算机中如何存储呢?比如字符0xabcd,它的存储格式到底是 AB CD,还是 CD AB 呢?
实际上两者都有可能,并分别有不同的名字。如果存储为 AB CD,则称为Big Endian;如果存储为 CD AB,则称为Little Endian。
具体来说,以下这种存储格式为Big Endian,因为值(0xabcd)的高位(0xab)存储在前面:
地址 | 值 |
0x00000000 | AB |
0x00000001 | CD |
相反,以下这种存储格式为Little Endian:
地址 | 值 |
0x00000000 | CD |
0x00000001 | AB |
UCS-2和UCS-4Unicode是为整合全世界的所有语言文字而诞生的。任何文字在Unicode中都对应一个值,这个值称为代码点(code point)。代码点的值通常写成 U+ABCD 的格式。而文字和代码点之间的对应关系就是UCS-2(Universal Character Set coded in 2 octets)。顾名思义,UCS-2是用两个字节来表示代码点,其取值范围为 U+0000~U+FFFF。
为了能表示更多的文字,人们又提出了UCS-4,即用四个字节表示代码点。它的范围为 U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一样的。
要注意,UCS-2和UCS-4只规定了代码点和文字之间的对应关系,并没有规定代码点在计算机中如何存储。规定存储方式的称为UTF(Unicode Transformation Format),其中应用较多的就是UTF-16和UTF-8了。
UTF-16和UTF-32UTF-16不难猜到,UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian或Little Endian方式直接保存下来。UTF-16包括三种:UTF-16,UTF-16BE(Big Endian),UTF-16LE(Little Endian)。
UTF-16BE和UTF-16LE不难理解,而UTF-16就需要通过在文件开头以名为BOM(Byte Order Mark)的字符来表明文件是Big Endian还是Little Endian。BOM为U+FEFF这个字符。
其实BOM是个小聪明的想法。由于UCS-2没有定义U+FFFE,因此只要出现 FF FE 或者 FE FF 这样的字节序列,就可以认为它是U+FEFF,并且可以判断出是Big Endian还是Little Endian。
举个例子。“ABC”这三个字符用各种方式编码后的结果如下:
UTF-16BE | 00 41 00 42 00 43 |
UTF-16LE | 41 00 42 00 43 00 |
UTF-16(Big Endian) | FE FF 00 41 00 42 00 43 |
UTF-16(Little Endian) | FF FE 41 00 42 00 43 00 |
UTF-16(不带BOM) | 00 41 00 42 00 43 |
Windows平台下默认的Unicode编码为Little Endian的UTF-16(即上述的 FF FE 41 00 42 00 43 00)。你可以打开记事本,写上ABC,然后保存,再用二进制编辑器看看它的编码结果。
另外,UTF-16还能表示一部分的UCS-4代码点——U+10000~U+10FFFF。表示算法比较复杂,简单说明如下:
- 从代码点U中减去0x10000,得到U'。这样U+10000~U+10FFFF就变成了 0x00000~0xFFFFF。
- 用20位二进制数表示U'。 U'=yyyyyyyyyyxxxxxxxxxx
- 将前10位和后10位用W1和W2表示,W1=110110yyyyyyyyyy,W2=110111xxxxxxxxxx,则 W1 = D800~DBFF,W2 = DC00~DFFF。
例如,U+12345表示为 D8 08 DF 45(UTF-16BE),或者08 D8 45 DF(UTF-16LE)。
但是由于这种算法的存在,造成UCS-2中的 U+D800~U+DFFF 变成了无定义的字符。
UTF-32UTF-32用四个字节表示代码点,这样就可以完全表示UCS-4的所有代码点,而无需像UTF-16那样使用复杂的算法。与UTF-16类似,UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码,UTF-32也同样需要BOM字符。仅用'ABC'举例:
UTF-32BE | 00 00 00 41 00 00 00 42 00 00 00 43 |
UTF-32LE | 41 00 00 00 42 00 00 00 43 00 00 00 |
UTF-32(Big Endian) | 00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43 |
UTF-32(Little Endian) | FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00 |
UTF-32(不带BOM) | 00 00 00 41 00 00 00 42 00 00 00 43 |
UTF-8UTF-16和UTF-32的一个缺点就是它们固定使用两个或四个字节,这样在表示纯ASCII文件时会有很多00字节,造成浪费。而RFC3629定义的UTF-8则解决了这个问题。 UTF-8用1~4个字节来表示代码点。表示方式如下:
UCS-2 (UCS-4) | 位序列 | 第一字节 | 第二字节 | 第三字节 | 第四字节 |
U+0000 .. U+007F | 00000000-0xxxxxxx | 0xxxxxxx | | | |
U+0080 .. U+07FF | 00000xxx-xxyyyyyy | 110xxxxx | 10yyyyyy | | |
U+0800 .. U+FFFF | xxxxyyyy-yyzzzzzz | 1110xxxx | 10yyyyyy | 10zzzzzz | |
U+10000..U+1FFFFF | 00000000-000wwwxx-
xxxxyyyy-yyzzzzzzz | 11110www | 10xxxxxx | 10yyyyyy | 10zzzzzz |
可见,ASCII字符(U+0000~U+007F)部分完全使用一个字节,避免了存储空间的浪费。而且UTF-8不再需要BOM字节。
另外,从上表中可以看出,单字节编码的第一字节为[00-7F],双字节编码的第一字节为[C2-DF],三字节编码的第一字节为[E0-EF]。这样只要看到第一个字节的范围就可以知道编码的字节数。这样也可以大大简化算法。
作者: Qter 时间: 2020-2-17 00:36
符号对应表查询:https://home.unicode.org/ https://www.unicode.org/releases/index.html
Unicode字符列表:https://www.ltool.net/characters ... plified-chinese.php
中日韩(CJK)汉字Unicode编码表:http://www.chi2ko.com/tool/CJK.htm
欢迎光临 firemail (http://firemail.wang:8088/) |
Powered by Discuz! X3 |