java字符编码-Unicode编码问题刨根究底
博客搬家: java字符编码问题
前段时间在读《java核心技术卷一》,遇到一些名词:码点、代码单元等,其实字面意思不难理解,解释如下
- 码点(code point):Unicode编码表中某个字符对应的代码值
- 代码单元(code unit):用于UTF-16编码的最小单元,16个bit
注意上述只是针对java中字符和字符串的Unicode+UTF-16机制的解释。若是其他编码方式就另说,如UTF-8的代码单元是用8个bit编码。
下面问题来了
书中建议,尽量不要使用char类型,最好将字符串转化为抽象数据类型来处理,即codepoints数组
//将String转化为码点数组
int[] codePoints = str.codePoints().toArray();
那么为什么要这样做呢,像c语言那样直接使用char数组不好吗?当然不行,因为在Unicode+UTF-16这种机制中,一个码点可由一个代码单元表示,但很多特殊字符也可能由两个代码单元表示。而char类型只能是一个代码单元。所以,若字符串中存在特殊字符,遍历char数组或者使用charAt等方法时就会出问题。但使用码点数组就OK,因为数组中每一个元素都代表一个码点,而不是一个代码单元。而c语言中采用ASCII字符集,每个码点都由一个8bit代码单元表示,使用char数组则不存在这个问题。
这样的描述对懂Unicode和UTF-16的人来说,很容易理解。但我想在我的博客中深究一下编码机制背后的原理。为了便于小白理解,先介绍一下编码规范的基本概念。
编码规范
制定编码规范为了将计算机能识别的二进制数,映射成人类能识别的字符。依据编码规范,计算机就可以将二进制数显示成字符。常见的编码规范有,ASCII码、GBK、ISO-8859-1、Unicode等。编码规范中有三个子概念,字库表、字符集、编码方式。
字库表
字库表中存储该编码规范能表示的所有字符。一套编码规范不一定能表示世界上所有的字符。例如GBK规范可以显示汉字,但不能显示法语、俄语等。
字符集
字库表中每一个字符都有一个二进制地址,字符集就是这些二进制数的集合。例如 00000000 - 01111111 为ASCII字符集的范围。
编码方式
某些编码规范包括大量的字符,例如Unicode中包含上百万个字符。若每个字符都采用同样长度的二进制数来编码,即定长编码,则要用三个字节来存储,甚至在将来会用到四个字节。这样很多本身只需要单字节存储的字符也会占用三四个字节,会导致极大的资源浪费。
如果能采用一些算法,使得部分字符采用单字节编码,部分采用双字节等(即变长编码),可节省不少资源,这些算法即为编码方式。常见的编码方式有UTF-8、UTF-16、UTF-32等。我前文所说的:Unicode+UTF-16机制,就容易理解了,即基于Unicode编码规范并采用UTF-16编码方式的机制。java中的字符串和字符正是采用这种机制进行编码的,下面详细介绍Unicode和UTF-16。
注意UTF的全称是Unicode Transformation Format,含义为将Unicode转换为某种格式。所以UTF-8、UTF-16等都是针对Unicode来说的。
Unicode
在Unicode出现之前,已经有了很多编码规范,如美国的ASCII码、中国的GBK、西欧的ISO-8859-1等,每一种规范都不能涵盖所有国家的语言。Unicode设计的初衷就是将所有语言中的字符进行统一编码。
Unicode最早的1.0版本中,字符集数量远远不到65536,因为当时的字符集不是那么庞大,使用2个字节编码足够使用,java也正是此时引进了16位的Unicode字符集。
然而,在Unicode增加了大量的汉语、日语、韩语字符之后,字符数量超过了65536,于是16位的char类型也就不能满足了。实际上,这些海量的Unicode字符可以被划分为17个代码级别(code plane)
- 第一级别,基本多语言级别(basic multilingual plane),范围U+0000~U+FFFF,下文称其为基本平面
- 其它16个级别,范围U+10000~U+10FFFF,存储辅助字符,下文把它称作增补平面
UTF-16编码则是对不同的代码级别做文章,采用不同长度的编码表示不同代码级别的码点
UTF-16
UTF-16使用16位作为一个代码单元。基本平面的码点采用一个代码单元进行编码,增补平面的码点使用两个代码单元。
可能你会问,使用一对代码单元表示一个增补平面的字符时,有没有可能把它判别成两个基本平面的码点?也就是说,有没有可能出现冲突的情况?
- 当然不会,UTF-16中使用了代理机制,即使用基本平面中未映射字符的字符集区域,来作为增补平面中字符的代码单元的区域
这些码点区域称为“替代区域”(syrrogate area),即U+D800 ~ U+DFFF范围,该区域在基本平面中属于空闲区域,2048个值。如此一来,便避免了冲突。
该替代区域分割为两部分,U+D800 ~ U+DBFF用于第一个代码单元,U+DC00 ~ U+DFFF用于第二个代码单元。
| 代码单元1 | 代码单元2 |
|---|---|
| 1101 10pp ppxx xxxx | 1101 11xx xxxx xxxx |
pppp是指16个级别的级别编号,24=16。两个代码单元的变数部分(p和x)共20位,可表示220=1048576个码点。而增补平面范围U+10000~U+10FFFF恰好也是1048576个码点。所以这两个代码单元可完美表示出增补平面中的所有字符。
这种方式十分巧妙。
事实上,只有UTF-16使用了“替代区域”方法,像现在被广泛接纳的UTF-8编码,是通过首字节的比特位判断码点的代码单元数量。
最后简单介绍下UTF-16和UTF-8的区别,以后再找时间细究,把UTF-8的坑填上。
- UTF-16使用2个或4个字节进行编码,大部分汉字采用两个字节编码,少量汉字采用四个字节
- UTF-8使用1个到4个字节编码,大部分汉字采用三个字节
java字符编码-Unicode编码问题刨根究底的更多相关文章
- java中文和unicode编码相互转换(转)
工具类代码如下: package aa.com; import java.io.UnsupportedEncodingException; public class UnicodeUtil { pub ...
- Python如何将字符和Unicode编码转变
小小总结一下,以防过几天忘记,自己的复习资料,如果能帮到大家,也是有所作用!! 1,字符转化为Unicode编码方法: ord("字符") ord("A") o ...
- C# 获取字符的Unicode编码
using UnityEngine;using System.Collections;using System.Collections.Generic; List<); string chars ...
- 字符串及其操作,字符的Unicode编码
plainText=input('message:') for c in plainText: print(chr(ord(c)-3),end='') plainText=input('message ...
- Java 字符转Unicode
static String unicode2String(String unicodeStr) { StringBuffer sb = new StringBuffer(); String str[] ...
- 【字符编码】Java字符编码详细解答及问题探讨
一.前言 继上一篇写完字节编码内容后,现在分析在Java中各字符编码的问题,并且由这个问题,也引出了一个更有意思的问题,笔者也还没有找到这个问题的答案.也希望各位园友指点指点. 二.Java字符编码 ...
- Java实现 中文转换成Unicode编码 和 Unicode编码转换成中文
想要实现中文字符转换为Unicode编码的话主要用到的是一个这样的包,自己可以去API文档里面查看下的 java.util.Properties; 直接进入主题吧,主要是 package Test01 ...
- Java 字符编码(二)Java 中的编解码
Java 字符编码(二)Java 中的编解码 java.nio.charset 包中提供了一套处理字符编码的工具类,主要有 Charset.CharsetDecoder.CharsetEncoder. ...
- java中文乱码解决之道(三)-----编码详情:伟大的创想---Unicode编码
随着计算机的发展.普及,世界各国为了适应本国的语言和字符都会自己设计一套自己的编码风格,正是由于这种乱,导致存在很多种编码方式,以至于同一个二进制数字可能会被解释成不同的符号.为了解决这种不兼容的问题 ...
随机推荐
- 「洛谷P1262」间谍网络 解题报告
P1262 间谍网络 题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意 ...
- 【5min+】 什么?原来C#还有这两个关键字
系列介绍 简介 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的. ...
- 1053 住房空置率 (20 分)C语言
在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断.判断方法如下: 在观察期内,若存在超过一半的日子用电量低于某给定的阈值 e,则该住房为"可能空置&quo ...
- js菜单栏切换
先来看看需要实现的需求: 这是某购物网站上经常看到的效果 我们把网页的模型抽象出来,下面是我实现的效果图: 源代码仅供大家参考,具体如下: <!DOCTYPE html> <html ...
- LEFT函数使用
详解:LEFT函数用于从一个文本字符串的第一个字符开始返回指定个数的字符 1.提取A2单元格从左往右2位字符 2.函数使用用途: LEFT函数用于从一个文本字符串的第一个字符开始返回指定个数的字符 语 ...
- Asp.Net Core下的开源任务调度平台ScheduleMaster
从何说起 2017年初的时候,由于当时项目需要做了一个乞丐版定时调度系统,那时候只在单机上实现了核心的调度功能.做这个玩意之前也调研了社区中开源的解决方案,找了几个实地部署试跑了一下,其实都很不错.但 ...
- Linux环境下详细讲解部署MySQL5.7版本
说明: 在本人写作这篇安装MySQL文章时,虽然MySQL已经发布到8.0.17版本,但对于行业来说,主力版本依然是5.7版本.目前在Linux环境默认安装时,大部分已经默认安装到8版本了,所以本人特 ...
- Android/Unity大乱斗-完整双方集成交互指南
这是一个很长很长的story!-芝麻粒儿创作 开篇 源码地址:GitHub 本文目的,将Unity集成到Android端,学完本文后你可以做到 Android任意布局加载Unity 3D场景 任意操作 ...
- 重拾c++第一天(1):环境配置
时过多年,c++基本不记得了,故在此记录相关重拾记录. 学习语言第一步当然是环境配置了(笑),由于暂无用c++进行大型项目开发的需求,所以先下载dev进行过渡. 安装过程非常简单,值得注意的是配置时选 ...
- .net Core 使用IHttpClientFactory请求
导读:本文已添加在晨曦微服务之旅,现在自己在尝试微服务架构,一边学边做项目快速的进入状态.当然在学习的过程中会将自己学到的知识进行分享. 一.为什么不用HttpClient ...