精华内容
下载资源
问答
  • utf-16
    万次阅读
    2018-12-10 12:11:49

    简介

    实际项目开发中,我们有时候可能需要将字符串转换成字节数组,而转化字节数组跟编码格式有关,不同的编码格式转化的字节数组不一样。下面列举了java支持的几种编码格式:

    • US-ASCII Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set
    • ISO-8859-1 ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
    • UTF-8 Eight-bit UCS Transformation Format
    • UTF-16BE Sixteen-bit UCS Transformation Format, big-endian byte order
    • UTF-16LE Sixteen-bit UCS Transformation Format, little-endian byte order
    • UTF-16 Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark

    本文重点讲解的是 UTF-16 编码格式字节数组的转化。UTF-16 顾名思义,就是用两个字节表示一个字符。那么用两个字节表示必然存在字节序的问题,即大端小端的问题。下面就来讲讲 UTF-16BEUTF-16LEUTF-16 三者之间的区别吧。

    UTF-16BE,其后缀是 BEbig-endian,大端的意思。大端就是将高位的字节放在低地址表示。
    UTF-16LE,其后缀是 LElittle-endian,小端的意思。小端就是将高位的字节放在高地址表示。
    UTF-16,没有指定后缀,即不知道其是大小端,所以其开始的两个字节表示该字节数组是大端还是小端。即FE FF表示大端,FF FE表示小端。

    代码测试

    TestUTF_16.java

    public static void main(String[] args) {
        String test = "代码";
    
        try {
            //UTF-16BE编码格式 大端形式编码
            byte[] bytesUTF16BE = test.getBytes("UTF-16BE");
            printHex("UTF-16BE", bytesUTF16BE);
    
            //UTF-16LE编码格式 小端形式编码
            byte[] bytesUTF16LE = test.getBytes("UTF-16LE");
            printHex("UTF-16LE", bytesUTF16LE);
    
            //UTF-16
            byte[] bytesUTF16 = test.getBytes("UTF-16");
            printHex("UTF-16", bytesUTF16);
            
            //大端编码格式的字节数组转化
            String strUTF16BE = new String(bytesUTF16BE, "UTF-16BE");
            String strUTF16LE = new String(bytesUTF16BE, "UTF-16LE");
            String strUTF16 = new String(bytesUTF16BE, "UTF-16");
            System.out.println("strUTF16BE:"+strUTF16BE);
            System.out.println("strUTF16LE:"+strUTF16LE);
            System.out.println("strUTF16:"+strUTF16);
            
            //小端编码格式的字节数组转化
            strUTF16BE = new String(bytesUTF16LE, "UTF-16BE");
            strUTF16LE = new String(bytesUTF16LE, "UTF-16LE");
            strUTF16 = new String(bytesUTF16LE, "UTF-16");
            System.out.println("strUTF16BE:"+strUTF16BE);
            System.out.println("strUTF16LE:"+strUTF16LE);
            System.out.println("strUTF16:"+strUTF16);
            
            //自带大小端编码格式的字节数组转化
            strUTF16BE = new String(bytesUTF16, "UTF-16BE");
            strUTF16LE = new String(bytesUTF16, "UTF-16LE");
            strUTF16 = new String(bytesUTF16, "UTF-16");
            System.out.println("strUTF16BE:"+strUTF16BE);
            System.out.println("strUTF16LE:"+strUTF16LE);
            System.out.println("strUTF16:"+strUTF16);
            
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    
    private static void printHex(String type, byte[] data) {
        StringBuilder builder = new StringBuilder();
        builder.append("type:").append(type).append(":   ");
        int temp = 0;
        for (int i = 0; i < data.length; i++) {
            temp = data[i] & 0xFF;
            builder.append("0x").append(Integer.toHexString(temp).toUpperCase()).append(" ");
        }
    
        System.out.println(builder.toString());
    }
    

    在这里插入图片描述

    从上面的测试结果可以看出来,指定大小端编码格式的,转化为字节数组时不会带FE FF或者FF FE。带有FE FF或者FF FE的字节数组可以转化成指定大小端编码格式的字符串。

    源码

    看看String源码中对getBytes的实现

    /**
     * Returns a new byte array containing the characters of this string encoded using the named charset.
     *
     * <p>The behavior when this string cannot be represented in the named charset
     * is unspecified. Use {@link java.nio.charset.CharsetEncoder} for more control.
     *
     * @throws UnsupportedEncodingException if the charset is not supported
     */
    public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
        return getBytes(Charset.forNameUEE(charsetName));
    }
    /**
     * Returns a new byte array containing the characters of this string encoded using the
     * given charset.
     *
     * <p>The behavior when this string cannot be represented in the given charset
     * is to replace malformed input and unmappable characters with the charset's default
     * replacement byte array. Use {@link java.nio.charset.CharsetEncoder} for more control.
     *
     * @since 1.6
     */
    public byte[] getBytes(Charset charset) {
        String canonicalCharsetName = charset.name();
        if (canonicalCharsetName.equals("UTF-8")) {
            return Charsets.toUtf8Bytes(value, offset, count);
        } else if (canonicalCharsetName.equals("ISO-8859-1")) {
        	return Charsets.toIsoLatin1Bytes(value, offset, count);
        } else if (canonicalCharsetName.equals("US-ASCII")) {
            return Charsets.toAsciiBytes(value, offset, count);
        } else if (canonicalCharsetName.equals("UTF-16BE")) {
            return Charsets.toBigEndianUtf16Bytes(value, offset, count);
        } else {
            CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
            ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
            byte[] bytes = new byte[buffer.limit()];
            buffer.get(bytes);
            return bytes;
        }
    }
    
    更多相关内容
  • CodeDetectorDetect File encoding, Now support UTF-8,GBK,UTF-16LE,UTF-16BE, Java检查文件编码,主要针对中文文本。目前支持 UTF-8(BOM或无BOM)、GBK、UTF-16LE、UTF-16BE单个文件或文件夹,支持编码批量转换。...
  • 例如,txt文件由UTF-16LE转为UTF-8…… 问题重现:往往利用python处理数据时,都会遇到文件编码格式不对,那么如何批量转换呢,下面直接上代码! 实例:整个文件夹的txt文件由UTF-16LE转为UTF-8 # coding utf-8 ...
  • ChangeEncoding change file encoding in batches, now support UTF-8,GBK,UTF-16LE,UTF-16BE, Java java -Dfile.encoding=utf-8 -mx1000m -jar ChangeEncoding-...1.1 添加新的编码UTF16LE(UTF-16LE)、UTF16BE(UTF-16
  • utf-16编码表

    2018-06-01 19:29:17
    有61726个字符,csv格式,做编码的基础数据 0000, 0001,Ā 0002,Ȁ 0003,̀ 0004,Ѐ 0005,Ԁ 0006,؀ 0007,܀ 0008,ࠀ 0009,ऀ 000C,ఀ 000D,ഀ 000F,ༀ 0010,က 0011,ᄀ 0012,ሀ
  • 包含GBK和UTF16字符集的对照表,用于将GBK字节流和UTF字符串之间的互相转换。
  • 最近需要对Linux与Windows平台下的字符传输出现乱码,对...参考了网上的UTF-8/UTF-16转换的资料,只有0x10000以下的Unicode编码进行了转换;对其代码进行了修改和补充,可以实现所有的UTF-8/UTF-16的转换,分享给大家。
  • UTF-16汉字编码表

    2016-07-10 08:50:33
    UTF-16汉字编码表,txt格式的
  • UTF16 UTF-16 UTF16BE UTF-16BE UTF16LE UTF-16LE UTF32 UTF-32 UTF32BE UTF-32BE UTF32LE UTF-32LE UCS2 UCS-2 UCS-2LE UCS-2BE UCS-2-INTERNAL UCS4 UCS-4 UCS-4LE UCS-4BE UCS-4-INTERNAL ...
  • Unicode 和 UTF-8、UTF-16UTF-32之间的关系

    万次阅读 多人点赞 2019-03-15 21:41:05
    Unicode 和 UTF-8、UTF-16UTF-32之间的关系 要厘清它们之间的关系就要先从编码开始说起: ASCII码 我们都知道,在计算机的世界里,信息的表示方式只有 0 和 1,但是我们人类信息表示的方式却与之大不相同,很多时候...

    Unicode 和 UTF-8、UTF-16、UTF-32之间的关系

    要厘清它们之间的关系就要先从编码开始说起:

    ASCII码

    我们都知道,在计算机的世界里,信息的表示方式只有 0 和 1,但是我们人类信息表示的方式却与之大不相同,很多时候是用语言文字、图像、声音等传递信息的。
    那么我们怎样将其转化为二进制存储到计算机中,这个过程我们称之为编码。更广义地讲就是把信息从一种形式转化为另一种形式的过程。
    我们知道一个二进制有两种状态:”0” 状态 和 “1”状态,那么它就可以代表两种不同的东西,我们想赋予它什么含义,就赋予什么含义,比如说我规定,“0” 代表 “吃过了”, “1”代表 “还没吃”。
    这样,我们就相当于把现实生活中的信息编码成二进制数字了,并且这个例子中是一位二进制数字,那么 2 位二进制数可以代表多少种情况能?对,是四种,2^2,分别是 00、01、10、11,那7种呢?
    答案是2^7=128。
    我们知道,在计算机中每八个二进制位组成了一个字节(Byte),计算机存储的最小单位就是字节,字节如下图所示 :

    在这里插入图片描述
    所以早期人们用 8 位二进制来编码英文字母(最前面的一位是 0),也就是说,将英文字母和一些常用的字符和这 128 中二进制 0、1 串一一对应起来,比如说 大写字母“A”所对应的二进制位“01000001”,转换为十六进制为 41。
    在美国,这 128 是够了,但是其他国家不答应啊,他们的字符和英文是有出入的,比如在法语中在字母上有注音符号,如 é ,这个怎么表示成二进制?
    所以各个国家就决定把字节中最前面未使用的那一个位拿来使用,原来的 128 种状态就变成了 256 种状态,比如 é 就被编码成 130(二进制的 10000010)。
    为了保持与 ASCII 码的兼容性,一般最高位为 0 时和原来的 ASCII 码相同,最高位为 1 的时候,各个国家自己给后面的位 (1xxx xxxx) 赋予他们国家的字符意义。
    但是这样一来又有问题出现了,不同国家对新增的 128 个数字赋予了不同的含义,比如说 130 在法语中代表了 é,但是在希伯来语中却代表了字母 Gimel(这不是希伯来字母,只是读音翻译成英文的形式)具体的希伯来字母 Gimel 看下图
    在这里插入图片描述
    所以这就成了不同国家有不同国家的编码方式,所以如果给你一串二进制数,你想要解码,就必须知道它的编码方式,不然就会出现我们有时候看到的乱码 。

    Unicode的出现

    Unicode 为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF (十六进制),有 110 多万,每个字符都有一个唯一的 Unicode 编号,这个编号一般写成 16 进制,在前面加上 U+。例如:“马”的 Unicode 是U+9A6C。
    Unicode 就相当于一张表,建立了字符与编号之间的联系。
    在这里插入图片描述
    它是一种规定,Unicode 本身只规定了每个字符的数字编号是多少,并没有规定这个编号如何存储。
    有的人会说了,那我可以直接把 Unicode 编号直接转换成二进制进行存储,是的,你可以,但是这个就需要人为的规定了,而 Unicode 并没有说这样弄,因为除了你这种直接转换成二进制的方案外,还有其他方案,接下来我们会逐一看到。
    编号怎么对应到二进制表示呢?有多种方案:主要有 UTF-8,UTF-16,UTF-32。

    1、UTF-32

    这个就是字符所对应编号的整数二进制形式,四个字节。这个就是直接转换。 比如马的 Unicode 为:U+9A6C,那么直接转化为二进制,它的表示就为:1001 1010 0110 1100。
    这里需要说明的是,转换成二进制后计算机存储的问题,我们知道,计算机在存储器中排列字节有两种方式:大端法和小端法,大端法就是将高位字节放到低地址处,比如 0x1234, 计算机用两个字节存储,一个是高位字节 0x12,一个是低位字节 0x34,它的存储方式为下:
    在这里插入图片描述
    UTF-32 用四个字节表示,处理单元为四个字节(一次拿到四个字节进行处理),如果不分大小端的话,那么就会出现解读错误,比如我们一次要处理四个字节 12 34 56 78,这四个字节是表示 0x12 34 56 78 还是表示 0x78 56 34 12?不同的解释最终表示的值不一样。
    我们可以根据他们高低字节的存储位置来判断他们所代表的含义,所以在编码方式中有 UTF-32BE 和 UTF-32LE,分别对应大端和小端,来正确地解释多个字节(这里是四个字节)的含义。

    2、UTF-16

    UTF-16 使用变长字节表示
    ① 对于编号在 U+0000 到 U+FFFF 的字符(常用字符集),直接用两个字节表示。
    ② 编号在 U+10000 到 U+10FFFF 之间的字符,需要用四个字节表示。
    同样,UTF-16 也有字节的顺序问题(大小端),所以就有 UTF-16BE 表示大端,UTF-16LE 表示小端。

    3、UTF-8

    UTF-8 就是使用变长字节表示,顾名思义,就是使用的字节数可变,这个变化是根据 Unicode 编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。使用的字节个数从 1 到 4 个不等。
    UTF-8 的编码规则是:

    ① 对于单字节的符号,字节的第一位设为 0,后面的7位为这个符号的 Unicode 码,因此对于英文字母,UTF-8 编码和 ASCII 码是相同的。

    ② 对于n字节的符号 (n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为 10,剩下的没有提及的二进制位,全部为这个符号的 Unicode 码 。

    举个例子:比如说一个字符的 Unicode 编码是 130,显然按照 UTF-8 的规则一个字节是表示不了它(因为如果是一个字节的话前面的一位必须是 0),所以需要两个字节(n = 2)。
    根据规则,第一个字节的前 2 位都设为 1,第 3(2+1) 位设为 0,则第一个字节为:110X XXXX,后面字节的前两位一律设为 10,后面只剩下一个字节,所以后面的字节为:10XX XXXX。
    所以它的格式为 110XXXXX 10XXXXXX 。

    下面我们来具体看看具体的 Unicode 编号范围对应的 UTF-8 二进制格式
    在这里插入图片描述
    那么对于一个具体的 Unicode 编号,具体怎么进行 UTF-8 的编码呢?
    首先找到该 Unicode 编号所在的编号范围,进而可以找到与之对应的二进制格式,然后将该 Unicode 编号转化为二进制数(去掉高位的 0),最后将该二进制数从右向左依次填入二进制格式的 X 中,如果还有 X 未填,则设为 0 。
    比如:“马”的 Unicode 编号是:0x9A6C,整数编号是 39532,对应第三个范围(2048 - 65535),其格式为:1110XXXX 10XXXXXX 10XXXXXX,39532 对应的二进制是 1001 1010 0110 1100,将二进制填入进去就为:
    11101001 10101001 10101100 。

    在这里插入图片描述
    在这里插入图片描述
    由于 UTF-8 的处理单元为一个字节(也就是一次处理一个字节),所以处理器在处理的时候就不需要考虑这一个字节的存储是在高位还是在低位,直接拿到这个字节进行处理就行了,因为大小端是针对大于一个字节的数的存储问题而言的。

    综上所述,UTF-8、UTF-16、UTF-32 都是 Unicode 的一种实现。

    展开全文
  • UNICODE 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8个位和 16个位。于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码...

    ASII(American Standard Code for Information Interchange,美国信息互换标准代码)。

    (部分摘自 https://www.zhihu.com/question/23374078 作者:于洋

    https://www.zhihu.com/question/23374078/answer/24385963 作者:邱昊宇


    总结:

    • 中国人民通过对 ASCII 编码的中文扩充改造,产生了 GB2312 编码,可以表示6000多个常用汉字。
    • 汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK 编码,它包括了 GB2312 中的编码,同时扩充了很多。
    • 中国是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,继续把 GBK 编码扩充为 GB18030 编码。
    • 每个国家都像中国一样,把自己的语言编码,于是出现了各种各样的编码,如果你不安装相应的编码,就无法解释相应编码想表达的内容。
    • 终于,有个叫 ISO 的组织看不下去了。他们一起创造了一种编码 UNICODE ,这种编码非常大,大到可以容纳世界上任何一个文字和标志。所以只要电脑上有 UNICODE 这种编码系统,无论是全球哪种文字,只需要保存文件的时候,保存成 UNICODE 编码就可以被其他电脑正常解释。
    • UNICODE 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8个位和 16个位。于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码体积比较大,占电脑空间比较多,如果面向的使用人群绝大部分都是中国人,用 GBK 等编码也可以。

    当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。

    从128 到255这一页的字符集被称”扩展字符集

    在中国:

    规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “ GB2312“。GB2312 是对 ASCII 的中文扩展。
    于是我们不得不继续把GB2312 没有用到的码位找出来老实不客气地用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字
    符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “ DBCS“(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍: “一个汉字算两个英文字符!
    ISO (国际标谁化组织)废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。

    unicode(是编码字符集)

    从unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!同时,也都是统一的”两个字节
    unicode并不是用2个字节来表示字符串的 。只是unicode1.0版本的所有字符都是2个字节。

    现在的unicode编码在一百一十万左右,远远超过了2的16次方,也就是2个字节。

    ”汉”,的unicode值为0x6C49。

    为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位

    Unicode符号范围 | UTF-8编码方式

    (十六进制) | (二进制)
    —————————————————————–
    0000 0000-0000 007F | 0xxxxxxx

    0000 0080-0000 07FF | 110xxxxx 10xxxxxx

    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    UTF-8 顾名思义,是一套以 8 位为一个编码单位的可变长编码。会将一个码位编码为 1 到 4 个字节:
    U+ 0000 ~ U+ 007F: 0XXXXXXX
    U+ 0080 ~ U+ 07FF: 110XXXXX 10XXXXXX
    U+ 0800 ~ U+ FFFF: 1110XXXX 10XXXXXX 10XXXXXX
    U+10000 ~ U+1FFFF: 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
    
    根据上表中的编码规则,之前的「知」字的码位 U+77E5 属于第三行的范围:
           7    7    E    5    
        0111 0111 1110 0101    二进制的 77E5
    --------------------------
        0111   011111   100101 二进制的 77E5
    1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行)
    11100111 10011111 10100101 代入模版
       E   7    9   F    A   5
    
    这就是将 U+77E5 按照 UTF-8 编码为字节序列 E79FA5 的过程。反之亦然。



    UTF(是字符集编码)编码规则

    ”汉”字保存到计算机中(硬盘、内存),机器码并不是简单的转化为2进制 1101100 01001001。

    而是取决于用到的是哪种字符集编码

    如字符集编码是UTF-8,那么”汉”字在计算机内部保存的值为0xE6B189,也就是111001101011000110001001,可以看到”汉”字变成了3个字节。UTF-8用1-8个字节来保存unicode编码的字符。 
    而如果用UTF-16来保存,那么”汉”字仍为仍为0x6C49,也就是 1101100 01001001。UTF-16只能是选两字节或四字节来保存字符 
    而UTF-32就是把所有的字符都用32bit也就是4个字节来表示。 
    所以这就是编码字符集和字符集编码的区别。



    byte order mark (BOM)

    就是表名这个文件到底是LE还是BE的, https://www.zhihu.com/question/20167122

    所谓大小端,是指字节存储或传输时的顺序
    在UTF-16文件的头2个字节里做个标记: LE [0xFF, 0xFE], BE [0xFE, 0xFF]
    编码长度和方式不同。
    UTF-16B E: 16 位 UCS 转换格式,Big Endian(最低地址存放高位字节,符合人们的阅读习惯) 字节顺序
    UTF-16LE: 16 位 UCS 转换格式,Little-endian(最高地址存放高位字节) 字节顺序


    BOM(byte order mark)是为 UTF-16 和 UTF-32 准备的,用于标记字节序(byte order)。微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码明确区分开,但这样的文件在 Windows 之外的操作系统里会带来问题。

    「UTF-8」和「带 BOM 的 UTF-8」的区别就是有没有 BOM。即文件开头有没有 U+FEFF

    UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。
    所以不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯(顺便提一下:把带有 BOM 的小端序 UTF-16 称作「Unicode」而又不详细说明,这也是微软的习惯)。

    展开全文
  • 字符编码的概念(UTF-8、UTF-16UTF-32都是什么鬼)

    万次阅读 多人点赞 2017-11-30 17:11:56
    UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。 UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来...

    字符集为每个字符分配了一个唯一的编号,通过这个编号就能找到对应的字符。在编程过程中我们经常会使用字符,而使用字符的前提就是把字符放入内存中,毫无疑问,放入内存中的仅仅是字符的编号,而不是真正的字符实体。

     

    这就抛出了一个问题,如何才能将字符编号放入内存中呢?

     

    对于 ASCII 字符集,这很容易。ASCII 总共包含 128 个字符,用 7 个比特位(Bit)恰好能够存储,不过考虑到计算机一般把字节(Byte)作为基本单元,为了操作方便,我们不妨用一个字节(也就是 8 个比特位)来存储 ASCII。这样虽然浪费了一个比特位,但是读写效率提高了。

     

    但是对于 Unicode,问题就没有这么简单了。Unicode 目前已经包含了上百万的字符,位置靠前的字符用一个字节就能存储,位置靠后的字符用三个字节才能存储。我们可以为所有字符都分配三个字节的内存,也可以为编号小的字符分配一个字节或者两个字节的内存,而为编号大的字符分配三个字节的内存。

     

    这两种方案各有优缺点,请读者看下面的分析。

    字符集和字符编码不是一个概念,字符集定义了文字和二进制的对应关系,为字符分配了唯一的编号,而字符编码规定了如何将文字的编号存储到内存中。有的字符集在制定时就考虑到了编码的问题,是和编码结合在一起的;有的字符集只管制定字符的编号,至于怎么编码,是其他人的事情。

    方案1:为每个字符分配固定长度的内存

    一种方案是为每个字符分配固定长度的内存,并且这块内存要足够大,可以容纳下所有的字符编号。这种方案最简单,直接将字符编号放入内存中即可,不需要任何转换,并且以后在字符串中定位字符、修改字符都非常容易。

    字符串就是一串连续的字符序列,它们在内存中按次序挨着存放。在C语言中,字符串由双引号 " "包围起来。

    目前的 Unicode 已经收录了上百万的字符,至少需要三个字节才能容纳下所有的字符编号。假设字符串"A3中¥"的 Unicode 编码值(十六进制形式)分别是 2A、31、DA49、BB672C,那么它们在内存中的存储形式为:

     

    在几乎所有的字符集中,常用字符的编号往往比较小,罕见字符的编号往往比较大,包括 Unicode 在内。

     

    A和3是 ASCII 编码中的字符,Unicode 为了兼容 ASCII,在设计时刻意保留了原来 ASCII 中字符的编号,所以英文字母和阿拉伯数字在 Unicode 中的编号都非常小,用一个字节足以容纳。中是一个汉字,编号比较大,一般要用两个字节才能容纳。¥可以看做是一个极其少见,或者只有极少数地区才会使用到的字符,这样的字符编号往往比较大,有时候需要三个字节才能容纳。

    是人民币符号,是汉字文化的一部分,它和其它汉字一样,实际上是用两个字节存储的,不过这里我们为了演示,故意犯错地说它需要三个字节。

    上图中带灰色背景的字节是没有用到的字节,它们就是被浪费掉的一部分内存空间,这就是用固定长度的内存来存储字符编号的缺点:常用字符的编号都比较小,这种方案会浪费很多内存空间,对于以英文为主的国家,比如美国、加拿大、英国等,内存利用率甚至会低于 50%。

    方案2:为每个字符分配尽量少的内存

    既然上面的方案有缺点,那我们就来改进一下。改进的思路也很明确,就是把空闲的内存压缩掉,为每个字符分配尽量少的字节,例如,A和3分配一个字节足以,中分配两个字节足以,如下图所示:

    这样虽然没有了空闲字节,不浪费任何内存空间了,但是又出现新的问题了:如果我不告诉你,你怎么知道2A表示一个字符,而不是2A31或者2A31DA才表示一个字符呢?后面的字符也有类似的问题。

     

    对于第一种方案,每个字符占用的字节数是固定的,很容易区分各个字符;而这种方案,不同的字符占用的字节数不同,字符之间也没有特殊的标记,计算机是无法定位字符的。

     

    这种方案还需要改进,必须让不同的字符编码有不同的特征,并且字符处理程序也需要调整,要根据这些特征去识别不同的字符。

     

    要想让不同的字符编码有不同的特征,可以从两个方面下手:

    1) 一是从字符集本身下手,在设计字符集时,刻意让不同的字符编号有不同的特征。

     

    例如,对于编号较小的、用一个字节足以容纳的字符,我们就可以规定这个字符编号的最高位(Bit)必须是 0;对于编号较大的、要用两个字节存储的字符,我们就可以规定这个字符编号的高字节的最高位必须是 1,低字节的最高位必须是 0;对于编号更大的、需要三个字节存储的字符,我们就可以规定这个字符编号的所有字节的最高位都必须是 1。

     

    程序在定位字符时,从前往后依次扫描,如果发现当前字节的最高位是 0,那么就把这一个字节作为一个字符编号。如果发现当前字节的最高位是 1,那么就继续往后扫描,如果后续字节的最高位是 0,那么就把这两个字节作为一个字符编号;如果后续字节的最高位是 1,那么就把挨着的三个字节作为一个字符编号。

     

    这种方案的缺点很明显,它会导致字符集不连续,中间留出大量空白区域,这些空白区域不能定义任何字符。

     

    2) 二是从字符编号下手,可以设计一种转换方案,字符编号在存储之前先转换为有特征的、容易定位的编号,读取时再按照相反的过程转换成字符本来的编号。

     

    那么,转换后的编号要具备什么样的特征呢?其实也可以像上面一样,根据字节的最高位是 0 还是 1 来判断字符到底占用了几个字节。

     

    相比第一种方案,这种方案有缺点也有优点:

     

    • 缺点就是多了转换过程,字符在存储和读取时要经过转换,效率低;
    • 优点就是在制定字符集时不用考虑存储的问题,可以任意排布字符。

     

    Unicode 到底使用哪种编码方案

    Unicode 是一个独立的字符集,它并不是和编码绑定的,你可以采用第一种方案,为每个字符分配固定长度的内存,也可以采用第二种方案,为每个字符分配尽量少的内存。

     

    需要注意的是,Unicode 只是一个字符集,在制定的时候并没有考虑编码的问题,所以采用第二种方案时,就不能从字符集本身下手了,只能从字符编号下手,这样在存储和读取时都要进行适当的转换。

     

    Unicode 可以使用的编码有三种,分别是:

    • UFT-8:一种变长的编码方案,使用 1~6 个字节来存储;
    • UFT-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;
    • UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。

     

    UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。

    1) UTF-8

    UTF-8 的编码规则很简单:如果只有一个字节,那么最高的比特位为 0;如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。

     

    具体的表现形式为:

    • 0xxxxxxx:单字节编码形式,这和 ASCII 编码完全一样,因此 UTF-8 是兼容 ASCII 的;
    • 110xxxxx 10xxxxxx:双字节编码形式;
    • 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式;
    • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式。

     

    xxx 就用来存储 Unicode 中的字符编号。

     

    下面是一些字符的编码实例(绿色部分表示本来的 Unicode 编号):

     

    字符Næ
    Unicode 编号(二进制)010011101110011000101110 11101100
    Unicode 编号(十六进制)4EE62E EC
    UTF-8 编码(二进制)0100111011000011 1010011011100010 10111011 10101100
    UTF-8 编码(十六进制)4EC3 A6E2 BB AC

    对于常用的字符,它的 Unicode 编号范围是 0 ~ FFFF,用 1~3 个字节足以存储,只有及其罕见,或者只有少数地区使用的字符才需要 4~6个字节存储。

    2) UTF-32

    UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提高了效率。

    3) UTF-16

    UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储。

     

    对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。

     

    对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于 DC00~DFFF 之间的双字节存储。

     

    如果你不理解什么意思,请看下面的表格:

     

    Unicode 编号范围
    (十六进制)
    具体的 Unicode 编号
    (二进制)
    UTF-16 编码编码后的
    字节数
    0000 0000 ~ 0000 FFFFxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx2
    0001 0000---0010 FFFFyyyy yyyy yyxx xxxx xxxx110110yy yyyyyyyy 110111xx xxxxxxxx4

     

    位于 D800~0xDFFF 之间的 Unicode 编码是特别为四字节的 UTF-16 编码预留的,所以不应该在这个范围内指定任何字符。如果你真的去查看 Unicode 字符集,会发现这个区间内确实没有收录任何字符。

     

     

    UTF-16 要求在制定 Unicode 字符集时必须考虑到编码问题,所以真正的 Unicode 字符集也不是随意编排字符的。

    总结

    只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。

     

    如果你希望查看完整的 Unicode 字符集,以及各种编码方式,请猛击:https://unicode-table.com/cn/

    虽然这个网站有时候无法访问,但它是最好的一个查看 Unicode 字符集的网站。

    GB2312、Shift-JIS 等国家(地区)字符集怎么编码

    GB2312、GBK、Shift-JIS 等特定国家的字符集都是在 ASCII 的基础上发展起来的,它们都兼容 ASCII,所以只能采用变长的编码方案:用一个字节存储 ASCII 字符,用多个字节存储本国字符。

     

    以 GB2312 为例,该字符集收录的字符较少,所以使用 1~2 个字节编码。

    • 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0;
    • 对于中国的字符,使用两个字节存储,并且规定每个字节的最高位都是 1。

     

     

    由于单字节和双字节的最高位不一样,所以很容易区分一个字符到底用了几个字节。

    宽字符和窄字符(多字节字符)

    有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。

     

    有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符。

     

    Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。

    展开全文
  • Unicode编码详解(四):UTF-16编码

    千次阅读 2020-12-31 18:45:47
    Unicode编码详解(四):UTF-16编码 若觉得本文写得还可以,请多多关注本人所作书籍《C++语法详解》电子工业出版社出版,网盘地址: https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg 本文为原创文章,转载请注明出处...
  • UTF-16UTF-8的方法,防止文件有BOM头

    千次阅读 2019-01-27 10:25:07
    在读公司代码的时候,发现了一个UTF-16UTF-8的方法,这还是博主第一次见到这种方法,不由的好奇了起来。为什么要转,应用场景是什么呢?这里大家一起来探讨下 二、贴代码 /** * @desc UTF-16转为UTF-8编码, 必须...
  • Unicode、UTF-8、UTF-16之间的区别

    万次阅读 多人点赞 2018-08-29 14:51:24
    这样就出现了UTF-8,UTF16,UTF-32.原理和之前肯定是完全一样的,UTF-32就是把所有的字符都用32bit也就是4个字节来表示.然后UTF-8,UTF-16就视情况而定了.UTF-8可以选择1至8个字节中的任一个来表示.而UTF-16只能是选两字...
  • 编码:UTF-8编码、UTF-16编码规则

    千次阅读 2019-09-14 11:55:33
    UTF是"Unicode/UCS Transformation Format"的首字母缩写,...UTF-8和UTF-16都是可变长度的编码方式: UTF-8编码方案可能用1、2、3或4个字节表示一个unicode值。 UTF-16编码方案可能用2或4个字节表示一个unicode值。...
  • UTF-16编码详解

    万次阅读 2019-03-18 16:05:48
    首先我们来思考UTF-16的设计思路: 我们知道Unicode的范围为0x0~0x10FFFF 首先是BMP区间,也就是0x0~0xFFFF这段区间,正好16位就可以表示,也兼容,两全其美 那么超过BMP区间的怎么办呢? 也就是0xFFFF~0x10FFFF这段...
  • Javascript中的string类型使用UTF-16编码

    千次阅读 2019-05-31 11:23:01
    在JavaScript中,所有的string类型(或者被称为DOMString)都是使用UTF-16编码的。 MDN DOMString是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString 直接映射到 一个String。 将null传递...
  • JAVA中其实用的是UTF-16编码

    千次阅读 2021-04-10 16:22:32
    有人说内存中为了方便所以不用任何的字符编码,直接使用unicode,但其实unicode只是一个字符表,相当于一本密码本,只是一个数字,代表这个字符和编号的对应关系,它在储存中的具体实现是UTF-8,UTF16,UTF-32。...
  • 原文2链接:UTF8,UTF16,UTF32,UTF16-LE,UTF16-BE,GBK 之间的转换 文章1 最近遇到的麻烦事  charset里的问题, 一般我们都用unicode来作为统一编码, 但unicode也有多种表现形式  首先, 我们说的unicode, 其实...
  • C++ 读取 UTF-16 LE 格式的文件

    千次阅读 2021-11-16 15:56:21
    使用C++读取UTF-16 LE ,也就是宽字符集。按正常的读取 std::ifstream file(fileName.c_str()); 打印出的是乱码。 从网上找了好久,C++并没有像python那样一个函数就搞定的方法。只能一步一步来。 思路: 使用...
  • 用Java将UTF-16转换为ASCII

    千次阅读 2021-02-28 10:35:26
    小编典典这个怎么样:... // my UTF-16 stringStringBuilder sb = new StringBuilder(input.length());for (int i = 0; i < input.length(); i++) {char ch = input.charAt(i);if (ch <= 0xFF) {sb.appen...
  • #! python3 # encoding: utf-8 import os import chardet def strJudgeCode(str1): return chardet.detect(str1) ...def readFile(path): with open(path,'r',encoding='utf-16 le') as f: filecontent ...
  • Unicode字符的UTF-8、UTF-16UTF-32编码方式[总结].pdf
  • (Unicode) UTF-8与UTF-16之间转换

    万次阅读 2016-10-21 13:33:39
    printf("ERR, Utf8_To_Utf16----sourceExhausted: source=%p, extraBytesToRead=%d, sourceEnd=%p\n", source, extraBytesToRead, sourceEnd); result = sourceExhausted; break; } /* Do this check whether...
  • UTF-8、UTF-16UTF-32的区别

    千次阅读 2017-06-09 22:48:14
    UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、...
  • 问题描述:Python 打开txt文件时提示,.txt must be utf...说明我的txt文件编码是 UTF-16LE 格式的 转换编码格式 在命令中需要替换以下内容: UTF-16LE 源文件格式 UTF-8 目标文件格式 original_file_path 源文件地址
  • C++ 字符编码转换之UTF-8/UTF-16/UTF-32

    千次阅读 2022-02-27 15:41:28
    C++编码格式 参考链接(什么是辅助平面) 参考链接(什么是UCS-2) UCS-2对每一个Unicode码位使用2bytes字集(16位bit);...现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2byt
  • c++ UTF-8 UTF-16转换

    2013-05-01 12:35:57
    宽字节和多字节转换,利用C++实现UTF-8和UTF-16之间的转换,包括错误处理
  • c# UTF-16UTF-8 互转

    千次阅读 2018-10-01 10:56:00
    /// <summary> /// UTF-16UTF-8 /// </summary> /// <param name="str"></param> /// <returns></returns> public st...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 754,038
精华内容 301,615
关键字:

utf-16

友情链接: radigodp.zip