字符编码分析


[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的范围为0x0000100000

其编码结果如下:
在这里插入图片描述
这里只做一个基本的解释,具体原因参考相关文章中其他资料(解释很详细了)。

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 在0xd8000xe000之间,表示a不是基本多文本平面bmp(Basic Multilingual Plane)中的代码点codepPoint, 而是代理区内Surrogate Zone的代码点,代理的是0x100000x100000范围内的代码点,其属于增补平面内, 顺序上比bmp平面上的代码点大。

这就是utf-16比较char 大小的基础准则。
处在0xE0000xFFFF的x都要-0x0800, 会使其范围移动到0xD8000xF7FF;
处在0xD8000xDFFF的y都要+0x2000, 会使其范围移动到0xF8000xFFFF
这两个操作之后, y必然>x。。举几个边界案例。

aba,b比较a fix后b fix后a,b fix后比较
0xD8000xFFFFa < b0xF8000xF7FFa > b
0xD8000xD900a < b0xF8000xF900a < 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");

在这里插入图片描述


相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值