[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");

本文深入探讨了Java中字符编码和解码的问题,特别是UTF-16编码在处理特殊字符时可能出现的混淆。通过实例分析了[1,-1,2,-2]数组在解码和再编码过程中的变化,以及Unicode字符如😀的UTF-8和UTF-16编码方式。同时,介绍了Java中比较UTF-16字符串的逻辑,并讲解了字节序的概念,如Big_ENDIAN和Little_ENDIAN。最后,讨论了字符编码在Java中的应用和影响。

255

被折叠的 条评论
为什么被折叠?



