文档章节

关于编码字符集

lookno
 lookno
发布于 2017/01/28 20:28
字数 2651
阅读 14
收藏 0

参考资料:http://www.360doc.cn/article/9470897_205152817.html

                  https://www.zhihu.com/question/30977092

Unicode是一个字符集,就好像一个字典一样,收录了全世界的文字啥的,英文是charset;

ascii、gbk、utf-8、utf-16叫字符集编码,英文是encoding,例如utf-8和utf-16编码是Unicode字符集的实现,规定了字符在计算机中的具体编码规则,也就是二进制到底是什么样的,在计算机中的意思是,可以是在硬盘文件中,也可以是内存里。

昨天在看阮一峰大侠的es6,突然看到了es6对string的扩展,介绍了string.codepointat()之类的方法,还涉及到了编码方式,瞬间又懵逼了,编码这个玩意我看了一遍又一遍,每次都觉得懂了些,可下次再遇到编码的问题还是一头雾水,理解的还是太浅了啊!

所以我就好奇,在java里面这个编码是咋地个处理法?平时用idea coding的时候,文件都是用utf-8保存的,也就是说java源代码是用utf-8进行编码的,如下图。

那么javac在编译.java文件的时候也要按照utf-8编码方式进行读取,否则就有可能出现乱码。

那javac为什么知道这个文件是utf-8的呢,它特么肯定知不道,因为文件的编码方式并不储存在文件里,文件中存储的是内容的二进制表示。正常来说,如果开一个bash,运行javac Test.java,那么他使用的是操作系统默认的编码方式进行读取Test.java,对于windows中文系统来说一般是gbk,这个值在java代码中可以用System.getProperty("file.encoding")获取 。所以如果一个源文件的编码方式是utf-8,然后你直接javac它,绝逼会报下面的错误(如果你是win的话~):

public class test {
    public static void main(String args[]){
        char cchar = 'a';
        System.out.format("%x",(short)cchar);
    }
}

但是为啥我在idea里面写完,然后在idea里面运行,毛事没有呢?因为idea给咱处理了,指定了读取源文件的编码方式,我估计它是这么指定的:javac -encoding utf-8 test.java(记不住的话可以javac -help查看一下),这样的话System.getProperty("file.encoding")也会变成utf-8的。 编译完成,生成的.class文件默认应该是utf-8编码的。

这样javac读入.java并且生成.class文件算是清楚了(真的吗- -),但是还有一点我特么特别好奇也特别生气,就是如果源文件有语法错误,javac会将错误信息重定向到bash中(不知道这个说法对不对~),也就是javac的输出,总是乱码,我就想这个输出到底是什么编码的???

我是win7,开的git bash,系统默认编码是gbk,.java源文件是utf-8,bash解析输入并显示在屏幕上时使用的编码是utf-8。

然后我故意弄个语法错误,然后 javac -encoding utf-8 test.java 出现下面这个jb玩意(说鸡不说吧,文明你我他~)

看来输出错误信息的时候bash用utf-8解码显示有乱码啊!

把bash编码换成gbk试试

然后就正常显示了~

这说明啥呢?说明javac在把错误信息重定向到bash时,使用的操作系统默认的编码方式对数据转换后进行传输,这也理所应当,因为这里是一个边界,javac和操作系统打交道的边界,不管你javac用啥编码,如果你想把一些信息给操作系统,然后呈现给用户,那么你就必须尊重人家操作系统的编码方式,并且对输入数据进行正确的编码,要不操作系统怎么会正常显示输出呢?不过这个输出到操作系统的编码方式一开始我在System.getProperties()中没找到,只找到了一个sun.jnu.encoding是gbk,然后网上一搜,这个属性是影响文件名创建的编码的- - 。

后来我又实验了一下,发现javac -enciding utf-8 test.java设置的编码只是说明javac编译器读取.java文件时使用的编码方式,影响不到System.getProperty("file.encoding")这个属性。原来这个属性是启动jvm时可以设置的,默认是操作系统的编码方式。java -Dfile.encoding=xxx test 进行设置,而且这个属性就是java程序运行时与操作系统打交道时使用的编码方式!当然这个属性是影响不到javac的,因为javac要在java命令之前运行~~ 

java程序运行起来之前的编码就说到这里吧,接下来说说jvm中的编码。

jvm内部使用的字符集编码为utf-16,也就是字符在内存中的储存方式为utf-16对,就是16不是8,- -。现在utf-8这么火这么流行这么普及为啥不用8呢?好像是之前sun被unicode联盟坑了~具体咋回事可以左转bi乎问讯- -。就比如说一个String吧 ,实际的内容是存在String类中private final char value[]数组中的,这个char数组存的东东就是经过utf-16编码的数据!嗯,就是这样。感觉有些懵逼,现在企业开发一般全站都特么用utf-8,这咋jvm你内部自己用个utf-16呢,尴尬。用就用吧,我也没办法- -。一开始java用utf16,有个原因好像是它的编码方式是定长的,就用俩字节表示字符,java正好可以用一个char表示一个字符,完美啊。但天有不测风云,俩字节最多表示65536个字符,可全世界的语言里的字符不止这么多啊,光™汉语都不止这么多,于是人家utf-16又扩充了,java表示我™也跟进(被逼的),于是utf-16就用2字节或者4个字节表示字符了,跟™utf-8一样也成了变长编码了- -。不过我就想不明白了,如果有一串字节,用它来表示一个字符串,那™怎么解析啊,到底是读取2字节作为一个字符还是读取4字节作为一个字符啊,咋™区分呢?其实这样的,不知道是unicode联盟还是sun(估计是unicode),整了一个规定,unicode码空间U+0000到U+FFFF为BMP(Basic Multilingual Plane基本多语言面),U+10000之后的码空间对应补充字符,然后为了正确读取2字节字符(也就是bmp中的)和4字节字符(补充字符),规定U+D800到U+DFFF在bmp中不对应字符,让补充字符使用这一段。两个char 组成了surrogate pair,第一个char成为高代理部分(high-surrogates range uD900到uDBFF ,1024个),第二个char叫低代理部分,uDC00到uDFFF,也是1024个,1024*1024也就是1048576个补充字符,加上bmp65536-2048个字符,一共1112064个。我这里说的乱七八糟的,大家可以来这里看,这篇文章讲的老带劲了http://www.360doc.cn/article/9470897_205152817.html 。

上面扯了这么半天,其实涉及到一个代码点(Code Point)和代码单元(Code Unit)的概念问题.

(引用自上面网址) 代码点(Code Point)就是指Unicode中为字符分配的编号,一个字符只占一个代码点,例如我们说到字符“汉”,它的代码点是U+6C49.代码单元(Code Unit)则是针对编码方法而言,它指的是编码方法中对一个字符编码以后所占的最小存储单元。例如UTF-8中,代码单元是一个字节,因为一个字符可以被编码为1个,2个或者3个4个字节;在UTF-16中,代码单元变成了两个字节(就是一个char),因为一个字符可以被编码为1个或2个char(你找不到比一个char还小的UTF-16编码的字符,嘿嘿)。说得再罗嗦一点,一个字符,仅仅对应一个代码点,但却可能有多个代码单元(即可能被编码为2个char)。

java类库中有的方法是跟代码点打交道的,有的是跟代码单元打交道的,java中的代码点就是指的Unicode字符集的代码点了,代码单元自然指的是utf-16的代码单元。比如String.length( )返回的就是utf-16代码单元的数量,看以下源码:

    /**
     * Returns the length of this string.
     * The length is equal to the number of <a href="Character.html#unicode">Unicode
     * code units</a> in the string.
     *
     * @return  the length of the sequence of characters represented by this
     *          object.
     */
    public int length() {
        return count;
    }

也就是说,对于BMP中的字符来说,length可以代表字符的个数,但对于含有补充字符的字符串来说,length就不能反映出字符串中含有字符的真实个数了:

public class test {
    public static void main(String args[]){
        String str = "你好世界𠮷";
        System.out.println(str);
        System.out.println(str.length());
    }
}

𠮷 这个古怪的汉字是补充字符,在utf-16编码中用4个字节,也就是两个char来表示,看到了吧,length( )返回的是 6 哦~~  所以在一些用户注册的时候判断用户名长度,直接用length判断其实是有些小问题的,当然正常人是不会用补充字符的汉字的- - 另外在京东 淘宝注册的时候一个汉字被算作2个字符- -,而且不支持 '𠮷' 这种补充字符 - -  要真想求出字符串的代码点的数量也就是我们正常人所理解的字符的数量,可以用str.codePointCount(0,str.length-1)方法。

再比如对于str.charAt( int index) 这种index也是指的代码单元,比如返回index为0的char,也就是代码单元。

再比如 Character.toChars(0x2F81A) 这个方法的参数为unicode的代码点(code point),返回一个utf-16编码的char数组。

再比如str.getBytes()返回一个byte数组,这个byte数组的编码为操作系统默认的编码,也就是file.encoding对应的编码,这个方法也可以接受具体的编码作为参数来生成对应编码的byte数组。

等等等等,String类和Character类中有很多很多方法涉及代码点和代码单元,有时间的时候可以阅读以下源码的注释,了解一下。

© 著作权归作者所有

上一篇: 声明
下一篇: 第一篇博客
lookno
粉丝 0
博文 3
码字总数 2803
作品 0
私信 提问
ASCII和Unicode编码的区别

归纳: 具体解释: 最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。 但是要处理中...

skh2015java
2018/05/29
0
0
编码字符集

本文介绍了编码字符集的概念以及Java与编码字符集之间的关系,文章的内容来自于本人工作过程中的经验积累以及网络中的相关文章介绍,如果文章中有任何纰漏欢迎读者指正,让我们共同讨论学习J...

飞雪无痕
2016/02/16
21
0
字符集问题

本文介绍了编码字符集的概念以及Java与编码字符集之间的关系,文章的内容来自于本人工作过程中的经验积累以及网络中的相关文章介绍,如果文章中有任何纰漏欢迎读者指正,让我们共同讨论学习J...

飞雪无痕
2016/01/25
19
0
Java中的字符集编码入门(二)编码字符集与字符集编码的区别

需要再一次强调的是,无论历史上的UCS还是现如今的Unicode,两者指的都是编码字符集,而不是字符集编码。花费一点时间来理解好这件事,然后你会发现对所有网页的,系统的,编码标准之间的来回...

Java开发者
2010/05/05
118
0
怎样解决Java/J2EE中文问题

大部分程序员在编程中都遇到过Java中文问题,但是只要你知道了Java系统的中文问题原理,我们就可以对中文问题说拜拜。 最古老的解决方案是使用String的字节码转换,这种方案问题是不方便,我...

海同网校
2015/04/28
8
0

没有更多内容

加载失败,请刷新页面

加载更多

计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
今天
5
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
今天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
今天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
今天
10
0
再见 Spring Boot 1.X,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维...

Java技术剑
今天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部