最近在阅读 zoekt和lucene的代码, 发现自己对字符集,编码等信息的理解有时候总有些模糊. 赶紧百度了学习, 这里记录下.
字符集(charset): ASCII, GB..., Unicode, 仅仅是一个标准方案, 规定了每个字符的数字编号是多少,没有规定编号如何存储. Unicode是目前世界通用, 试图囊括所有的字符. GB是国家制定的.
Unicode诞生之前,还有个内码表, 可以忽略
在Unicode中, 每个字符对应的数字又叫做码点, code point, 是一个添加了 U+ 前缀的十六进制整数,如字母 A 的代码点就是 U+0041.
编码方案: 有的字符集没有规定码点对应的二进制码该如何存储, GB字符集有规定编码方案, 而Unicode没有, 为了合理利用空间,不同的编码方案被提出:
- UTF8: 根据码点大小自动调整, 使用1-4个字节, 且不区分大小断(endian)
- UFT16: 基本平面U+0000 - U+FFFF使用2字节存储, 辅助平面: U+10000 - U+10FFFF使用4个字节,且区分大小端.
- UTF32: 固定4字节, 区分大小端.
大小端
LE: little-endian, 小端
BE: big-endian, 大端
BOM(byte-order mark): 编码头, 字节顺序标志,插入到以UTF-8、UTF16或UTF-32编码文件开头的特殊标记,用来标记多字节编码文件的编码类型和字节顺序. 如下, 来源: https://www.cnblogs.com/wuqinglong/p/10329339.html
UTF16: 因为有的是2字节,有的是4字节,所以, 为了判断出解析的时候将4个字节当一个字符还是把2个字节当一个字符, UTF16采用了巧妙的方式: 一个17个平面,将基本平面的 U+D800 (55296)- U+DFFF(57343) 这些码点不对应任何字符, 所以不是每个平面内的每个位置都被分配给了指定的字符. 不对应任何的字符, 应该是无法显示出来, 但是可以存在, 如果二进制流中最后两个字节是: 1111 1000 0000 0001
, 即0xD8 0x01
, 还是能读到这样的未成对的字符.
U+D800 (55296)- U+DFFF(57343) 这部分可以称之为代理区, 其中 U+D800 ~ U+DBFF 这个部分称为 高位代理区(前导代理区),U+DC00 ~ U+DFFF 这个部分称为 低位代理区(后尾代理区)。
具体的计算方式: .... 忽略
为什么使用 FE FF 呢, 这是因为大端: 0xFEFF = 65279, 又叫做“ ZERO WIDTH NO-BREAK SPACE ”. 在UTF16编码下, 65279 会产生
BE: 0xFEFF
LE: 0xFFFE
通过判断他们的值就可以知道剩下的是大端还是小端了. 且貌似这个值不会出现在码点范围内?
解答自己的疑惑:
- Java中的char是两个字节,可以存汉字吗?
不是很合适, 有的汉字它存不了, 比如: '\uD842\DFB7‘ = 134071, 它需要两个char, 一共四个字节.另外, 可以看jdk的api文档:
The
char
data type (and therefore the value that aCharacter
object encapsulates) are based on the original Unicode specification, which defined characters as fixed-width 16-bit entities. The Unicode Standard has since been changed to allow for characters whose representation requires more than 16 bits. The range of legal code points is now U+0000 to U+10FFFF, known as Unicode scalar value. (Refer to the definition of the U+n notation in the Unicode Standard.)
In the Java SE API documentation, Unicode code point is used for character values in the range between U+0000 and U+10FFFF, and Unicode code unit is used for 16-bitchar
values that are code units of the UTF-16 encoding. For more information on Unicode terminology, refer to the Unicode Glossary.
Unicode code point 和 Unicode code unit 是不同的东西, 一个或者两个unit可能组成一个code point
- go就比较好了, 直接用rune来表示码点, 关于它的编码问题, emm,还没看 // todo
java的Character
类中有一些方法:
int codePointAt(char[] a, int index, int limit)
返回char[]中,从index 最多到 limit, 对应的unicode 码点
boolean isBmpCodePoint(int codePoint)
是否是基本面
boolean isSupplementaryCodePoint(int codePoint)
是否是补充...
static boolean isHighSurrogate(char ch)
判断这个char是不是高代理区的code unit
static boolean isLowSurrogate(char ch)
static boolean isSurrogate(char ch)
static boolean isSurrogatePair(char high, char low)
int charCount(int codePoint)
需要几个char来代表码点
static int toCodePoint(char high, char low)
static int codePointAt(CharSequence seq, int index)
static int codePointBefore(CharSequence seq, int index)
获取前面的
char highSurrogate(int codePoint)
char lowSurrogate(int codePoint)
char[] toChars(int codePoint)
static int codePointCount(CharSequence seq, int beginIndex, int endIndex)
static boolean isDefined(int codePoint)
boolean isISOControl(int codePoint)
static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset)
从index开始(闭区间), 往前(codePointOffset为负)或者往后(正), 吃掉codePointOffset个码点后新的codepoint所对应的开始char, 如下:
todo
阅读
lucene: org.apache.lucene.analysis.CharacterUtils
go的encoding
学习参考:
- https://blog.csdn.net/weixin_44198965/article/details/93125017
- https://blog.csdn.net/u011277123/article/details/53155897
- https://my.oschina.net/goldenshaw?tab=newest&catalogId=536953 (完善)
- https://stackoverflow.com/questions/20966802/utf-16-character-encoding-of-java