- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章一文解开java中字符串编码的小秘密(干货)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
简介 。
在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系,同时你还会了解变种UTF-8,并且探讨一下UTF-8和变种UTF-8在java中的应用.
一起来看看吧.
Unicode的发展史 。
在很久很久以前,西方世界出现了一种叫做计算机的高科技产品.
初代计算机只能做些简单的算数运算,还要使用人工打孔的程序才能运行,不过随着时间的推移,计算机的体积越来越小,计算能力越来越强,打孔已经不存在了,变成了人工编写的计算机语言.
一切都在变化,唯有一件事情没有变化。这件事件就是计算机和编程语言只流传在西方。而西方日常交流使用26个字母加有限的标点符号就够了.
最初的计算机存储可以是非常昂贵的,我们用一个字节也就是8bit来存储所有能够用到的字符,除了最开始的1bit不用以外,总共有128中选择,装26个小写+26个大写字母和其他的一些标点符号之类的完全够用了.
这就是最初的ASCII编码,也叫做美国信息交换标准代码(American Standard Code for Information Interchange).
后面计算机传到了全球,人们才发现好像之前的ASCII编码不够用了,比如中文中常用的汉字就有4千多个,怎么办呢?
没关系,将ASCII编码本地化,叫做ANSI编码。1个字节不够用就用2个字节嘛,路是人走出来的,编码也是为人来服务的。于是产生了各种如GB2312, BIG5, JIS等各自的编码标准。这些编码虽然与ASCII编码兼容,但是相互之间却并不兼容.
这严重的影响了国际化的进程,这样还怎么去实现同一个地球,同一片家园的梦想?
于是国际组织出手了,制定了UNICODE字符集,为所有语言的所有字符都定义了一个唯一的编码,unicode的字符集是从U+0000到U+10FFFF这么多个编码.
那么unicode和UTF-8,UTF-16,UTF-32有什么关系呢?
unicode字符集最后是要存储到文件或者内存里面的,直接存储的话,空间占用太大。那怎么存呢?使用固定的1个字节,2个字节还是用变长的字节呢?于是我们根据编码方式的不同,分成了UTF-8,UTF-16,UTF-32等多种编码方式.
其中UTF-8是一种变长的编码方案,它使用1-4个字节来存储。UTF-16使用2个或者4个字节来存储,JDK9之后的String的底层编码方式变成了两种:LATIN1和UTF16.
而UTF-32是使用4个字节来存储。这三种编码方式中,只有UTF-8是兼容ASCII的,这也是为什么国际上UTF-8编码方式比较通用的原因(毕竟计算机技术都是西方人搞出来的).
Unicode详解 。
知道了Unicode的发展史之后,接下来我们详解讲解一下Unicode到底是怎么编码的.
Unicode标准从1991年发布1.0版本,已经发展到2020年3月最新的13.0版本.
Unicode能够表示的字符串范围是0到10FFFF,表示为U+0000到U+10FFFF.
其中U+D800到U+DFFF的这些字符是预留给UTF-16使用的,所以Unicode的实际表示字符个数是216 − 211 + 220 = 1,112,064个.
我们将Unicode的这些字符集分成17个平面,各个平面的分布图如下:
以Plan 0为例,Basic Multilingual Plane (BMP)基本上包含了大部分常用的字符,下图展示了BMP中所表示的对应字符:
上面我们提到了U+D800到U+DFFF是UTF-16的保留字符。其中高位U+D800–U+DBFF和低位U+DC00–U+DFFF是作为一对16bits来对非BMP的字符进行UTF-16编码。单独的一个16bits是无意义的.
UTF-8 。
UTF-8是用1到4个字节来表示所有的1,112,064个Unicode字符。所以UTF-8是一种变长的编码方式.
UTF-8目前是Web中最常见的编码方式,我们看下UTF-8怎么对Unicode进行编码:
最开始的1个字节可以表示128个ASCII字符,所以UTF-8是和ASCII兼容的.
接下来的1,920个字符需要两个字节进行编码,涵盖了几乎所有拉丁字母字母表的其余部分,以及希腊语,西里尔字母,科普特语,亚美尼亚语,希伯来语,阿拉伯语,叙利亚语,Thaana和N'Ko字母,以及组合变音符号标记。BMP中的其余部分中的字符需要三个字节,其中几乎包含了所有常用字符,包括大多数中文,日文和韩文字符。Unicode中其他平面中的字符需要四个字节,其中包括不太常见的CJK字符,各种历史脚本,数学符号和表情符号(象形符号).
下面是一个具体的UTF-8编码的例子:
UTF-16 。
UTF-16也是一种变长的编码方式,UTF-16使用的是1个到2个16bits来表示相应的字符.
UTF-16主要在Microsoft Windows, Java 和 JavaScript/ECMAScript内部使用.
不过UTF-16在web上的使用率并不高.
接下来,我们看一下UTF-16到底是怎么进行编码的.
首先:U+0000 to U+D7FF 和 U+E000 to U+FFFF,这个范围的字符,直接是用1个16bits来表示的,非常的直观.
接着是:U+010000 to U+10FFFF 。
这个范围的字符,首先减去0x10000,变成20bits表示的0x00000–0xFFFFF.
然后高10bits位的0x000–0x3FF加上0xD800,变成了0xD800–0xDBFF,使用1个16bits来表示.
低10bits的0x000–0x3FF加上0xDC00,变成了0xDC00–0xDFFF,使用1个16bits来表示.
U' = yyyyyyyyyyxxxxxxxxxx // U - 0x10000 。
W1 = 110110yyyyyyyyyy // 0xD800 + yyyyyyyyyy 。
W2 = 110111xxxxxxxxxx // 0xDC00 + xxxxxxxxxx 。
这也是为什么在Unicode中0xD800–0xDFFF是UTF-16保留字符的原因.
下面是一个UTF-16编码的例子:
UTF-32 。
UTF-32是固定长度的编码,每一个字符都需要使用1个32bits来表示.
因为是32bits,所以UTF-32可以直接用来表示Unicode字符,缺点就是UTF-32占用的空间太大,所以一般来说很少有系统使用UTF-32. 。
Null-terminated string 和变种UTF-8 。
在C语言中,一个string是以null character ('\0')NUL结束的.
所以在这种字符中,0x00是不能存储在String中间的。那么如果我们真的想要存储0x00该怎么办呢?
我们可以使用变种UTF-8编码.
在变种UTF-8中,null character (U+0000) 是使用两个字节的:11000000 10000000 来表示的.
所以变种UTF-8可以表示所有的Unicode字符,包括null character U+0000.
通常来说,在java中,InputStreamReader 和 OutputStreamWriter 默认使用的是标准的UTF-8编码,但是在对象序列化和DataInput,DataOutput,JNI和class文件中的字符串常量都是使用的变种UTF-8来表示的.
补充知识:Java基础之字符串的编码(Encode)和解码(Decode) 。
废话不多说,看代码~ 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package
newFeatures8;
import
java.io.UnsupportedEncodingException;
import
java.util.Arrays;
/*
* 编码(由看得懂到看不懂):字符串变字节数组
* 解码(由看不懂到看得懂):字符数组变字符串
* String--》byte[];//str.getBytes();//str.getBytes(String CharsetName);
* byte[]--》String;//new String(byte[] bytes)//new String(byte[] bytes,String CharsetName);
*/
public
class
Practice {
public
static
void
main(String[] args) {
try
{
String s=
"你好"
;
//ISO-8859-1 根本就不识别中文
// byte[] bytes=s.getBytes("gbk");
// System.out.println(Arrays.toString(bytes));//[-60, -29, -70, -61]
//使用utf-8 编码每个字符占3个字节
//byte[] bytes=s.getBytes("utf-8");
// System.out.println(Arrays.toString(bytes));//[-28, -67, -96, -27, -91, -67]
// String s1=new String(s.getBytes("utf-8"), "gbk");//浣犲ソ
// String s1=new String(s.getBytes("gbk"), "utf-8");//???
//当网页已经出现乱码,而使用的Tomcat服务器,Tomcat服务器使用的是ISO-8859-1 只需要再编码解码即可
String s1=
new
String(s.getBytes(
"ISO-8859-1"
),
"utf-8"
);
System.out.println(s1);
//一般要养成一个习惯:就是全部用utf-8
}
catch
(UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package
newFeatures8;
import
java.io.UnsupportedEncodingException;
public
class
Practice {
public
static
void
main(String[] args)
throws
UnsupportedEncodingException {
getLowest8Bit();
}
/*
* 通过研究发现:当往记事本里写入"联通"两字时,保存后打开,发现出现乱码
* 原因是:当你写入中文时:记事本使用的是GBK(按照一个字符两个字节)编码
* ,当你打开记事本时,使用的是UTF-8(按照一个字符3个字节)解码
* 如何解决:只要在联通前加个汉字即可,不能是字母
*
* "联通"二字比较特殊
* 其二进制数的最低8位刚好符合UTF-8的解码格式
*/
public static void getLowest8Bit() throws UnsupportedEncodingException{
String s="联通";
byte[] bytes=s.getBytes("gbk");
for (byte b : bytes) {
//System.out.println(Integer.toBinaryString(b));
/*
11111111111111111111111111000001
11111111111111111111111110101010
11111111111111111111111111001101
11111111111111111111111110101000
*/
//通过使用 与上 &0xff 来获取其最低最低8位 0xff=255
System.out.println(Integer.toBinaryString(b&0xff));
/*
* 11000001
10101010
11001101
10101000
*/
//匹配到了utf-8 的标志位
//一个字节 标志位0打头
//两个字节 :第一个字节110打头,第二个字节10打头
//三个字节:第一个字节1110打头,第二个字节10打头,第三个字节10打头
}
}
}
|
以上这篇一文解开java中字符串编码的小秘密(干货)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/superfjj/article/details/108550193 。
最后此篇关于一文解开java中字符串编码的小秘密(干货)的文章就讲到这里了,如果你想了解更多关于一文解开java中字符串编码的小秘密(干货)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
是否可以在 Delphi 中解开这些名称?如果是这样,我从哪里可以获得更多信息? 在 dbrtl100.bpl 中找不到特定条目的错误消息示例我想知道它找不到哪个确切的函数(单元、类、名称、参数等)。
我是小白,如何从列表中删除引号和逗号?或者我如何“取消字符串”? 在不显示字典 (dic) 的情况下,我使用的代码如下所示: >>>import itertools >>>list(itertools
我正在学习构建器模式,到目前为止我了解到,它是用于初始化的常用模式的一个很好的替代方案: 伸缩构造函数模式 JavaBean 模式 问题是,我真的不喜欢从我的域模型中的对象中删除 getter 和 s
我有这段文字 "Welcome to my city. Hello my good friend" 使用 jQuery(或 javascript),我想解开所有 span 标签,只保留第一级,我的意思
在为大学撰写作业的过程中,我一直享受着学习新的 Haskell monad 的乐趣。耶!!! 我有一个可以很好地进行类型检查的函数: compile :: Prog -> State VarsStat
let myLabel:UILabel = UILabel(frame:CGRectMake(0, 0, 44, 44)) myLabel.text = "Primary"
是否有一个好的 npm 包可以删除在 NodeJS 服务器上运行的 html 字符串中不必要的嵌套标签(无浏览器 DOM)?我尝试过 sanitize-html,但似乎不可能做到这一点。 我收到用户发
我目前正在编写一些日志代码,这些代码应该——除其他外——打印有关调用函数的信息。这应该比较容易,标准C++有一个type_info类(class)。这包含 typeid 的类/函数/等的名称。但它被破
我有以下组件 const ListItem = ({ children, title = '' }) => { const [isActive, setIsActive] = useSta
我正在尝试取消在 python3 中 pickle 的对象。这在 python3 中有效,但在 python2 中无效。该问题可以重现到 pickle 协议(protocol) 0。示例代码: imp
这个问题的灵感来自 How to transform a flow chart into an implementation?它询问从代码中通过算法消除 goto 语句的方法。 answer this
当我的 child 执行 unwind segue 时,我的 Controller 的 viewDidAppear 被调用。 在这个方法中(只有这个方法,我需要知道它是否来自 unwind) 注意:
我用 C 编写了一个小程序来管理 SQLite3 中的书目数据库。到目前为止,这只是一个允许导入和导出 BibTeX 数据的命令行工具。为了使导出/导入功能更加可定制(例如,在导入时始终在日期字段中组
这个问题与How to split an array according to a condition in numpy?密切相关但我正在寻找一种更通用的方法来拆分给定未知数量索引的数组: impor
我是一名优秀的程序员,十分优秀!