[1,-1,2,-2]数组解编码
public static void main(String[] args) {
byte[] b = new byte[]{1, -1, 2, -2};
// debug进去,发现这里是解码,s是4个字符
// [, �, , �]
String s = new String(b);
// 这里再进行编码
// �是无法进行编码的,结果为统一的代号 -17, -65, -67
byte[] bytes = s.getBytes();
System.out.println(bytes);
}
结果如下
[1, -17, -65, -67, 2, -17, -65, -67]
s的四个字符分别是
?表示的字符是不可见乱码字符,再编码成字节数组时,占用3个字节,分别是-17,-65,-67
。负数在解码时都是不可见乱码字符,再编码时都会是
-17,-65,-67
,导致失真。
字符编码案例
从unicode字符百科上随机找到一个字符
😀,其code Point为U+1F600
, codePoint的范围为0x0000
到 100000
。
其编码结果如下:
这里只做一个基本的解释,具体原因参考相关文章中其他资料(解释很详细了)。
uft-8
0x1F600
用utf-8表示,需要4个字节,所以binary列 第一个11110xxx
,4个连续的1表示表示需要4个字节表示,后面每个字节以10
开头,将0x1F600
等价于0b 0001 1111 0110 0000 0000
填充到剩下的字节里,逆序填充,空的地方补0(深红色格子)。
如下图(注意观察同色格子):
utf-16BE
0x1F600
- 0x10000
= 0x0F600
, 转化成二进制分为高10位和低10位。
高10位 0x003D
+ 0xD800
= 0xD83D
低10位0x0200
+ 0xDC00
= 0xDE00
UTF-16BE,其后缀是 BE 即 big-endian,大端的意思。大端就是将高位的字节放在低地址表示。(不好解释,跟utf-16FE相反)
utf-16FE
跟utf-16BE
相反。
utf-32BE
跟uft-16BE
类似,存在大小端的概念,但是翻转粒度更大,如下图
Binary string comparison with UTF-16
代码片段截选自lucene UTF16SortedAsUTF8Comparator类
,用UTF16的编码顺序来比较两个char
数组。
public int compare(CharsRef a, CharsRef b) {
int aEnd = a.offset + a.length;
int bEnd = b.offset + b.length;
int i = FutureArrays.mismatch(a.chars, a.offset, aEnd,
b.chars, b.offset, bEnd);
//找到第一个不相等的字符
if (i >= 0 && i < Math.min(a.length, b.length)) {
// http://icu-project.org/docs/papers/utf16_code_point_order.html
char aChar = a.chars[a.offset + i];
char bChar = b.chars[b.offset + i];
/* aChar != bChar, fix up each one if they're both in or above the surrogate range, then compare them */
if (aChar >= 0xd800 && bChar >= 0xd800) {
if (aChar >= 0xe000) {
// 0x800 = 2* 2^10 = 2k=2048,
//(“代理区(Surrogate Zone)”,范围为0xD800~0xDFFF(十进制55296~57343),
// 共2048个码点未定义。UTF8和UTF32没有这个问题)
aChar -= 0x800;
} else {
// 0x2000 = 2^13 = 8k
aChar += 0x2000;
}
if (bChar >= 0xe000) {
bChar -= 0x800;
} else {
bChar += 0x2000;
}
}
/* now aChar and bChar are in code point order
* 强转为int,一定为正数,因为2个字节转4个字节,前面都是补0
*/
return (int)aChar - (int)bChar; /* int must be 32 bits wide */
}
// One is a prefix of the other, or, they are equal:
return a.length - b.length;
}
如果16进制的char a 在0xd800
到0xe000
之间,表示a不是基本多文本平面bmp(Basic Multilingual Plane)
中的代码点codepPoint
, 而是代理区内Surrogate Zone
的代码点,代理的是0x10000
到0x100000
范围内的代码点,其属于增补平面
内, 顺序
上比bmp平面上的代码点大。
这就是utf-16比较char 大小的基础准则。
处在0xE000
到 0xFFFF
的x都要-0x0800
, 会使其范围移动到0xD800
到0xF7FF
;
处在0xD800
到 0xDFFF
的y都要+0x2000
, 会使其范围移动到0xF800
到0xFFFF
。
这两个操作之后, y必然>x
。。举几个边界案例。
a | b | a,b比较 | a fix后 | b fix后 | a,b fix后比较 |
---|---|---|---|---|---|
0xD800 | 0xFFFF | a < b | 0xF800 | 0xF7FF | a > b |
0xD800 | 0xD900 | a < b | 0xF800 | 0xF900 | a < b |
Java中字节字符相关知识
java.nio.ByteOrder
字节序的大小端
//大端,字节序列在内存中排序时从低地址到高地址
public static final ByteOrder BIG_ENDIAN
= new ByteOrder("BIG_ENDIAN");
//小端,字节序列在内存中排序时从低地址到高地址
public static final ByteOrder LITTLE_ENDIAN
= new ByteOrder("LITTLE_ENDIAN");