千万别在年轻时假装不爱钱

小说:千万别在年轻时假装不爱钱作者:宗董扁更新时间:2019-03-26字数:31856

想通这一点,王小民不禁苦笑一声,说道:“胜男姐,你是不是怕我跟别人合作,而不跟你合作了?”

易经大师罗李华浅谈艺名

沐刚来到这里,心中也是有些激动。自己的大老板竟然单独召见他,并且说了给他一个天大的任务要做,他能不激动吗。
整兵两日,六万骑兵离开了居延海,穿过金山和乌德键山之间草原,沿着乌德键山山脉向西北方向绕去。

林秀容却像是猜到了许飞琼在想什么,于是微笑着道:“小姐不用担心,前来相助我们的这些人,有不少都是来自各地的散仙,妖族只占了其中一部分。而且他们也并非是以妖灵界的名义前来,而是慕风公子与飞琼小姐之名,前来投靠你们。风魂公子身为大荒境之主,各地散仙前来投靠他,希望在重建苍天时混个仙官,这种事连天庭也管不着。”

一、charAt 与 codePonitAt

我们知道 Java 内部使用的是 utf-16 作为它的 char、String 的字符编码方式,这里我们叫它内部字符集。而 utf-16 是变长编码,一个字符的编码被称为一个 code point,它可能是 16 位 —— 一个 code unit,也可能是 32 位 —— 两个 code unit。

作为一个输入法爱好者,我偶尔会编程处理一些生僻字。其中有些生僻字大概是后来才加入 unicode 字符集里的,直接用 charAt 方法读取它们,会得到一堆问号。原因很清楚 —— 因为这些字符是用两个 code unit,也就是两个 char 表示的。charAt 找不到对应的编码,就会将这些 char 输出成「?」。

//示例
public class Test {
    public static void main(String[] args){
        String s = "????";
        System.out.println(s.length());   //输出:2
        System.out.println(s.charAt(0));  //输出:?
        System.out.println(s.charAt(1));  //输出:?
    }
}

因此,涉及到中文,一定要使用 String 而不是 char,并且使用 codePoint 相关方法来处理它。否则的话,如果用户使用了生僻字,很可能就会得到不想要的结果。

下面是一个使用 codePoint 遍历一个字符串的示例,需要注意的是,codePoint 是 int 类型的,因此需要做些额外的转换:

public class Test {
    public static void main(String[] args){

        String s = "赵孟????";
        for (int i = 0; i < s.codePointCount(0,s.length()); i++) {
            System.out.println(
                    new String(Character.toChars(s.codePointAt(i))));
        }

    }
}

/* 结果:


        ????
*/

二、内部字符集与输出字符集(内码与外码)

现在我们知道了中文字符在 java 内部可能会保存成两个 char,可还有个问题:如果我把一个字符输出到某个流,它还会是两个 char,也就是 4 字节么?
回想一下,Java io 有字符流,字符流使用 jvm 默认的字符集输出,而若要指定字符集,可使用转换流。
因此,一个中文字符,在内部是使用 utf-16 表示,可输出就不一定了。
来看个示例:

import java.io.UnsupportedEncodingException;

public class Test {
    public static void main(String[] args)
            throws UnsupportedEncodingException {

        String s = "中";   //????
        System.out.println(s + ": chars: " + s.length());
        System.out.println(s + ": utf-8 bytes:" + s.getBytes("utf-8").length);
        System.out.println(s + ": utf-16 bytes: " + s.getBytes("utf-16").length);

    }
}

输出为:

中: chars: 1
中: utf-8 bytes:3
中: unicode bytes: 4
中: utf-16 bytes: 4


????: chars: 2
????: utf-8 bytes:4
????: unicode bytes: 6
????: utf-16 bytes: 6

一个「中」字,内部存储只用了一个 char,也就是 2 个字节。可转换成 utf-8 编码后,却用了 3 个字节。怎么会不一样呢,是不是程序出了问题?
当然不是程序的问题,这是内码转换成外码,字符集发生了改变,所使用的字节数自然也可能会改变。(尤其这俩字符集还都是变长编码)

三、utf-16、utf-16le、utf-16be,utf-8、bom

不知道在刚刚的示例中,你有没有发现问题:同是 utf-16,为何「中」和「????」在转换成 byte 后,都多出了两字节数据。
原因在于,utf-16 以 16 位为单位表示数据,而计算机是以字节为基本单位来存储/读取数据的。因此一个 utf-16 的 code unit 会被存储为两个字节,需要明确指明这两个字节的先后顺序,计算机才能正确地找出它对应的字符。而 utf-16 本身并没有指定这些,所以它会在字符串开头插入一个两字节的数据,来存储这些信息(大端还是小端)。这两个字节被称为BOM(Byte Order Mark)。刚刚发现的多出的两字节就是这么来的。
如果你指定编码为 utf-16le 或 utf-16be,就不会有这个 BOM 的存在了。这时就需要你自己记住该文件的大小端。。

四、更多

  1. 在 windows 中,utf-8 格式的文件也可能会带有 BOM,但 utf-8 的基本单位本来就是一个字节,因此它不需要 BOM 来表示 所谓大小端。这个 BOM 一般是用来表示该文件是一个 utf-8 文件。不过 linux 系统则对这种带 BOM 的文件不太友好。不般不建议加。。
  2. unicode字符集UCS(Unicode Character Set) 就是一张包含全世界所有文字的一个编码表,但是 UCS 太占内存了,所以实际使用基本都是使用它的其他变体。一般来说,指定字符集时使用的 unicode 基本等同于 utf-16.(所以你会发现第二节演示的小程序里,utf-16 和 unicode 得出的结果是一样的。)

四、与 Python3 对比

python 3 在字符串表示上,做了大刀阔斧的改革,内部完全用 unicode 表示,因此 python 程序员完全不需要去考虑字符的底层表示的问题。len(str) 得到的就是 unicode 字符数。(不过在 windows 平台上,即使是 python,也要注意带不带 BOM 的问题。详细的请Google)

参考

java 语言中的一个字符占几个字节? - RednaxelaFX - 知乎
彻底搞懂字符编码(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian...)
Java_字符编码

本文允许转载,但要求附上源链接:Java 中文编码分析

编辑:杜开

发布:2019-03-26 08:12:05

当前文章:http://ayzwzx.cn/news/2019012612071/index.html

因妈重男轻女,和哥关系不好,咋办 起名专家罗李华谈起名之禁忌 罗李华谈:属狗的人2016年运程 孩子如何面对 大过年的双方家长互相挑剔做儿女的夹在中间za办? 你了解恋人的依恋风格吗? 今日中元节,你可知它的核心含义? 全国各地骗子骗术有何不同?

63563 93361 31090 90350 41687 44926 42597 81939 72073 83463 42550 95298 23551 57245 94196 97665 96783 64471 97269 99872

我要说两句: (0人参与)

发布