当前位置: 首页 > news >正文

从String源码看Java中的编码

从String源码的一个构造方法说起

public String(int[] codePoints, int offset, int count) {}

what?codePoints是什么鬼?为了看懂这个源码,有必要了解一个这个codePoints(代码点)的相关知识,其实整个String源码都会不少的涉及的java编码的相关知识,比如indexOf(int ch, int fromIndex)

img_b72bf222324c35fef5c1ea1e2c387845.png
一脸懵逼

为什么会有Unicode

​ 学C/C++的时候我们知道了ASCII码,但这个能表示的字符有限,后来又出现了一些乱七八糟的编码表,Unicode就是企图统一一下编码而产生的。

Unicode的介绍

​ Unicode的第一个版本是用2个字节来编码所有的字符的,因为编码者们认为2^16=65536能容纳世界上所有语言,后来他们发现他们错了,哈哈哈,第二个版本就用4个字节来编码所有字符,这个后面说。

img_bea3dbc3aaed270db06e274576c2d1cb.jpe

先以第一个版本来说,用2个字节来编码所有字符(这个要很清晰,不然会有点懵),正好UTF-16也是这么弄的,大家误解就以为Unicode就是UTF-16。这里会涉及两个步骤,一个步骤是字符与编码一一对应的问题,如a对97,b对98如此;另一个是如何将编码的二进制这些01串保存起来的问题,这就实现了UTF(unicode transformation format),有UTF-8,UTF-16……

UTF-8与UTF-16

​ UTF16很好理解,在第一个版本的Unicode中,就是2个字节保存一个字符。UTF8就不同,它可能用1个/2个/3个来表示。那我怎么知道它用来多少个自己来表示呢?这就需要一个规定:

  1. 0开头的,就表示1个字节表示一个字符,即0xxx xxxx,如0101 0011
  2. 110x xxxx 10xx xxxx这种表示把2个字节当成一个单元,表示一个字符。
  3. 1110 xxxx 10xx xxxx 10xx xxxx这种表示3个字节当成一个单元,表示一个字符。

由上面我们可以看出UTF-8需要判断每个字节中的开头标志信息,所以如果一当某个字节在传送过程中出错了,就会导致后面的字节也会解析出错.而UTF-16不会判断开头标志,即使错也只会错一个字符,所以容错能力强.

​ 从上面可以看出,当1个字节表示一个字符时,能表示2^7=128个字符,2个字节表示时能表示2048个字符,3个单元表示时能表示65536个字符。由于"汉"的编码27721大于2048了所有两个字节还不够,只能用三个字节来表示。

接着看看第二个版本

​ 相关规定:

  • Unicode 统一编码 0x000000-0x10ffff,其中0x0000-0xffff为基本多语言平面字符

  • bmp 基本多语言平面字符,对应Unicode中0x0000-0xffff

  • Unicode码空间为U+0000到U+10FFFF,一共有17个平面,每个平面可容下65536个code point。也就是17 * 65536=1,114,112。但是其中的U+D800-U+DFFF作为UTF-16编码代理区保留,也就是它们不会作为code point分配给字符,保留数目是8 * 256=2048。

    看到这里,我们可以知道:

  • 如果在BMP级别中,那么16bits(一个代码单元)就足够表示出字符的Unicode值。

  • 那不在的呢,就属于增补字符了。那就是需要4个字节来表示,那4个字节为啥不是能表示0x00000000-0xffffffff个字符呢,只能表示到0x10ffff呢,这就有点像UTF-8的意思了,占用几个位置来表示我需要用4个字节来显示这个字符,那么需要占用多少位呢?因为只需要表示0x10 ffff - 0x10000 = 0xf ffff即可,就是20位,也就是说,虽然你给我分配4个字节32位这么多,但我只需要20位就足够表示0x10000~0x10FFFF,那剩下12位怎么办呢,这样吧,每2个字节用6位作为代理区,剩下10位用作编码,这就是U+D800-U+DFFF作为UTF-16编码代理区保留的规定了。

  • 那为什么是0xD800~0xDFFF呢,我们来看看,假设我们是编码者,我们现在要留一段来做代理区,假设我做代理区的起点选了0xD800吧,对应的是1100 1000 0000 0000,那我把剩下10位编码上去,就能表示:1101 1000 0000 0000~1101 1011 1111 1111即0xD800~ 0xDBFF,这个就算高位代理区了,然后同理,0xDC00~ 0xDFFF就算地位代理区了。

  • 好了,BMP区域有2048个编码被用作代理区了,所以它们是不能用来表示任何字符的。

Last but not least

简要的讨论了一下以String构造方法引出的一点问题,其实java的编码还是蛮复杂的,一口气说太多太复杂,估计大脑cpu也不够用了。

相关文章:

  • 怎样让html加载完毕后加载js代码
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Vmware虚拟机的单用户模式
  • Flask学习笔记(2)-login_page
  • shell安全防范———慎将当前目录.加入PATH~~~之~隔壁老王来敲门
  • Java:泛型
  • Myth源码解析系列之一-项目简介
  • 在地铁上看了zabbix 的书发现 报警执行远程命令
  • Python中级 —— 01面向对象进阶
  • Ansible批量修改root密码(playbook)
  • 健忘?科学家想用机器学习+电击实验,帮你增强记忆力
  • mysql 设置自增id起始值
  • 多迪技术总监告诉你为什么人工智能用Python?
  • python之路----面向对象的封装特性
  • DAY9-字符串笔记整理2018-1-19
  • [数据结构]链表的实现在PHP中
  • 〔开发系列〕一次关于小程序开发的深度总结
  • Android交互
  • Apache Spark Streaming 使用实例
  • co模块的前端实现
  • Create React App 使用
  • ES10 特性的完整指南
  • HTML中设置input等文本框为不可操作
  • java8 Stream Pipelines 浅析
  • JavaScript 基础知识 - 入门篇(一)
  • JavaScript-Array类型
  • JavaScript创建对象的四种方式
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Linux CTF 逆向入门
  • Markdown 语法简单说明
  • MobX
  • mockjs让前端开发独立于后端
  • Object.assign方法不能实现深复制
  • Promise面试题2实现异步串行执行
  • React组件设计模式(一)
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Vue官网教程学习过程中值得记录的一些事情
  • 初探 Vue 生命周期和钩子函数
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 嵌入式文件系统
  • 驱动程序原理
  • 跳前端坑前,先看看这个!!
  • ​io --- 处理流的核心工具​
  • ​什么是bug?bug的源头在哪里?
  • (1)Android开发优化---------UI优化
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (6)STL算法之转换
  • (70min)字节暑假实习二面(已挂)
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (十三)Flask之特殊装饰器详解
  • (四)c52学习之旅-流水LED灯
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB