精华内容
下载资源
问答
  • 2019-11-04 16:17:06

    不知道大家有没有思考过这样的问题,一个字节为什么是8位呀,也许还有小伙伴不知道我说的这些是什么,没关系往下看。

    第一个解释(历史)是IBM为System/360设计了一套8位EBCDIC编码,涵盖了数字、大小写字母和大部分常用符号,同时又兼容广泛用于打孔卡的6位BCDIC编码。

    第二个解释,二进制我们应该都知道,在ASCII表中,存储的所有字符,用的是8位的二进制,0到127是,128个字符。最后个是01111111是删除键。如果在多加一个就不是8位了。

    以上我把一个字节为什么是8位解决了。

    下面说一下,为什么int是占4个字节

    第一个解释是int据说是根据不同的编译器在定义的,不同的编译器int的字节是不一样的,但是大多的编译器int都占4个字节。

    第二个解释是操作系统16位的时候,int 2字节,操作系统32位的时候,int 4字节,由于32位系统之前占主流地位,实际现在就算是64位系统,出于兼容性考虑,int也是4字节的。

    数据类型占内存的位数实际上与操作系统的位数和编译器(不同编译器支持的位数可能有所不同)都有关。

    更多相关内容
  • Java一个汉字几个字节(详解与原理)(转载)

    万次阅读 多人点赞 2019-05-29 23:33:35
    背景: 今天学习Netty做定长消息发送时,发现到UTF-8编码下的中文并非两字节,是三字节,omg~,遂翻了篇博客后才发现原来java中文对应的字节长度还有这么多说道,涨姿势了,咳咳~ ...不同的编码格式占字节数是...

    背景:

    今天学习Netty做定长消息发送时,发现到UTF-8编码下的中文并非两个字节,是三个字节,omg~,遂翻了篇博客后才发现原来java中文对应的字节长度还有这么多说道,涨姿势了,咳咳~

    原文如下: 忒长了,原文作者大大辛苦了,各位看官捡感兴趣的瞅,java新手遇到编码问题可看后面关于编码问题的说明,应该会有所收获~

    (啰嗦完了。。。)

     

    1、先说重点:

    不同的编码格式占字节数是不同的,UTF-8编码下一个中文所占字节也是不确定的,可能是2个、3个、4个字节;

    2、以下是源码:

    @Test
        public void test1() throws UnsupportedEncodingException {
            String a = "名";
            System.out.println("UTF-8编码长度:"+a.getBytes("UTF-8").length);
            System.out.println("GBK编码长度:"+a.getBytes("GBK").length);
            System.out.println("GB2312编码长度:"+a.getBytes("GB2312").length);
            System.out.println("==========================================");
    
            String c = "0x20001";
            System.out.println("UTF-8编码长度:"+c.getBytes("UTF-8").length);
            System.out.println("GBK编码长度:"+c.getBytes("GBK").length);
            System.out.println("GB2312编码长度:"+c.getBytes("GB2312").length);
            System.out.println("==========================================");
    
            char[] arr = Character.toChars(0x20001);
            String s = new String(arr);
            System.out.println("char array length:" + arr.length);
            System.out.println("content:|  " + s + " |");
            System.out.println("String length:" + s.length());
            System.out.println("UTF-8编码长度:"+s.getBytes("UTF-8").length);
            System.out.println("GBK编码长度:"+s.getBytes("GBK").length);
            System.out.println("GB2312编码长度:"+s.getBytes("GB2312").length);
            System.out.println("==========================================");
        }

    3、运行结果

    UTF-8编码长度:3
    GBK编码长度:2
    GB2312编码长度:2
    ==========================================
    UTF-8编码长度:4
    GBK编码长度:1
    GB2312编码长度:1
    ==========================================
    char array length:2
    content:|  ? |
    String length:2
    UTF-8编码长度:4
    GBK编码长度:1
    GB2312编码长度:1
    ==========================================

     4、几种编码格式的简单介绍

    几种编码格式。

    • ASCII 码

    学过计算机的人都知道 ASCII 码,总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。

    • ISO-8859-1

    128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。

    • GB2312

    它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。

    • GBK

    全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。

    • GB18030

    全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与 GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。

    • UTF-16

    说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。Unicode 是 Java 和 XML 的基础,下面详细介绍 Unicode 在计算机中的存储形式。

    UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

    • UTF-8

    UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。

    UTF-8 有以下编码规则:

    1. 如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
    2. 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
    3. 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字

    5、字符编码的历史故事

    很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们认为8个开关状态作为原子单位很好,于是他们把这称为"字节"。 

    再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出更多的状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为"计算机"。 

    开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。 

    他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端设备或者打印机遇上这些约定好的字节时,就要做一些约定的动作。遇上 00x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,对于终端就用彩色显示字母。他们看到这样很好,于是就把这些0x20(十进制32)以下的字节状态称为"控制码"。 

    他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的 文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的"Ascii"编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。 

    后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们用到的许多字母在ASCII中根本没有,为了也可以在计算机中保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧! 

    等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉,并且规定:一个小于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的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍: 

    "一个汉字算两个英文字符!一个汉字算两个英文字符……" 

    因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么"倚天汉字系统"才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办? 

    真是计算机的巴比伦塔命题啊! 

    正在这时,大天使加百列及时出现了——一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。 

    UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ascii里的那些"半角"字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高 8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。 

    这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是 的,从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不同, "字节"是一个8位的物理存贮单元,而"字符"则是一个文化相关的符号。在UNICODE 中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。 

    从前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。UNICODE 对于他们来说是一个很好的一揽子解决方案,于是从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,把所有的核心代码都改成了用 UNICODE 方式工作的版本,从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。 

    但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。 

    如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧! 

    UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到 UTF时并不是直接的对应,而是要过一些算法和规则来转换。 

    受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构;而另一些是采用高位先发送的方式。在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符——如果之后的文本是高位在位,那就发送"FEFF",反之,则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节? 

    下面是Unicode和UTF-8转换的规则 

    Unicode 
       
    UTF-8 
       
    0000 - 007F 
       
    0xxxxxxx 
       
    0080 - 07FF 
       
    110xxxxx 10xxxxxx 
       
    0800 - FFFF 
       
    1110xxxx 10xxxxxx 10xxxxxx

    例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。 

    讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。 

    其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。 

    当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码: 

    检测文件头标识,提示用户选择,根据一定的规则猜测 

    最标准的途径是检测文本最开头的几个字节,开头字节 Charset/encoding,如下表: 

    EF BB BF UTF-8 
       
    FF FE UTF-16/UCS-2, little endian 
       
    FE FF UTF-16/UCS-2, big endian 
       
    FF FE 00 00 UTF-32/UCS-4, little endian. 
       
    00 00 FE FF UTF-32/UCS-4, big-endian.

    当你新建一个文本文件时,记事本的编码默认是ANSI(代表系统默认编码,在中文系统中一般是GB系列编码), 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,"联通"的内码是: 

    c1 1100 0001 
       
    aa 1010 1010 
       
    cd 1100 1101 
       
    a8 1010 1000

    注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的, 

    于是当我们再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因。 

    而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。

    6、一个字符为什么占两个字节

     public static void main(String[] args) {
         System.out.printf("The max value of type char is %d.%n",
                 (int)Character.MAX_VALUE);
         System.out.printf("The min value of type char is %d.%n",
                 (int)Character.MIN_VALUE);
     }

    运行上面的程序,输出

      The max value of type char is 65535.
      The min value of type char is 0.
    说明char的范围从0到65535,那么正好是两个字节所能表示的范围(65535十六进制就是0xFFFF,一个字节能表示0~0xFF,两个字节能表示0~0xFFFF),所以说一个char占两个字节。
    那么char的值到底是什么呢?比如当我这样写char c = '放';

    public static void main(String[] args) throws Exception {
        char c = '放';
        System.out.printf("The value of char %c is %d.%n", c, (int)c);
          
        String str = String.valueOf(c);
        byte[] bys = str.getBytes("Unicode");
        for (int i = 0; i < bys.length; i++) {
            System.out.printf("%X ", bys[i]);
        }
        System.out.println();
          
        int unicode = (bys[2] & 0xFF) << 8 | (bys[3 & 0xFF]);
        System.out.printf("The unicode value of %c is %d.%n", c, unicode);
    }

    运行输出:
      The value of char 放 is 25918.
      FE FF 65 3E 
      The unicode value of 放 is 25918.
    首先你看到,这个char的值是25918,那他是什么呢?先不管它,接着我把这个char放在一个String里,并进行Unicode编码,得到四个字节FE FF 65 3E,前面两个实际上与内容无关,是BOM,即字节序标识,FE FF表示是Big Endian,也就是高位在前,低位在后,所以按照这个规则,讲653E转换为10进制int,发现最后输出25918,也就是这个字符的Unicode值是25918,所以你现在知道一个char到底存储的是什么了吧。

    至于GBK,UTF-8,UTF-16的关系,我先抛开GBK,因为它有点特殊。
    首先你要知道UTF-8和UTF-16还有UTF-32是为了方便传输和存储的而产生的对Unicode字符的编码方式。
    先说UTF-8,随着全球化Unicode流行起来,不管你做什么,支持Unicode都将是潮流,就算你可能永远也用不到,但这对西方国家就不太好,因为以前ASCII字符集,一个字符只需要一个字节,而现在用Unicode一个英文字母也需要两个字节,如果需要传输和存储,那会浪费一半的空间或流量,所以就想出了一种变长编码方式,那就是UTF-8,它对ASCII字符集内的字符,只用一个字节编码,而其他字符按照一定规则进行两、三、四字节编码,具体规则是:
    Unicode编码(十六进制)    UTF-8 字节流(二进制)
    000000 - 00007F                0xxxxxxx
    000080 - 0007FF                110xxxxx 10xxxxxx
    000800 - 00FFFF               1110xxxx 10xxxxxx 10xxxxxx
    010000 - 10FFFF               11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    但这样做一些东方国家不干了,因为他们的字符基本都是在000800 - 00FFFF这个区间,用UTF-8反倒要多用一个字节,总共需要三个字节才能表示,而且用UTF-8处理他们的字符,不能直接转换,需要做一些运算,以‘放’为例,它的Unicode码是25918,二进制表示是0110010100111110,如果要转成UTF-8,首先取高四位0110,和1110拼接,组成11100110,然后中间六位010100,与10拼接构成10010100,最后低六位111110,与10拼接构成10111110,所以三个字节是11100110 10010100 10111110,也就是十六进制的E6  94 BE,也就是你上面写的-26 -108 -66。可以看到这个运算量虽然不大,基本是位操作,但如果你每个字符都要这么操作实在是有损效率,综合这几点考虑,于是又弄了一个UTF-16,不严谨地来说它等价于Unicode原生编码,它统一采用双字节表示一个字符(其实有四字节区域,但现在一般没有用到),而由于它用多字节表示,和Unicode一样需要字节序标识,你上面代码里发现它得到-2, -1, 101, 62,转为十六进制就是FE FF 65 3E,和我第二个实例程序中相同,说明UTF-16的码值(如表示‘放’的65 3E)和Unicode原生编码是相同的。

    UTF-32的诞生其实也不奇怪,因为UTF-16还是一个变长编码方式,一个字符可能由两个或四个字节表示,有些有强迫症的人总觉得不好,所以为了他们就有了UTF-32,它统一使用四字节表示一个字符,因为用得不多所以不详细说了。

    最后说说GBK是个什么东西。GBK是国标扩(展)的拼音首字母,是我国在1995年制定的专门针对汉语和一些少数名族语言的编码方式,和Unicode之间没有一一对应的关系,也就是说Unicode中有的字符GBK不一定有,GBK有的字符Unicode也不一定有,而且GBK和Unicode中共有字符,他们的编码值没有一种简单的对应关系,也就是无法通过简单计算得到,只能通过查表转换。为什么会有GBK这种奇葩呢?其实是当时Unicode还没制定好,更没在全球范围内推广,而中国人要用电脑总不可能永远用英语吧?所以我国就自行制定了一个国标,当时是GB2312,(其实台湾地区针对繁体还有一个Big5,但这里就不详述了),GB2312后来增加了很多字符,包括很多少数名族的语言,成为了一个新的编码标准,那就是GBK。

    7、深入分析 Java 中的中文编码问题(转载)

    原文链接:http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/#ibm-pcon

    Java 中需要编码的场景

    前面描述了常见的几种编码格式,下面将介绍 Java 中如何处理对编码的支持,什么场合中需要编码。

    I/O 操作中存在的编码

    我们知道涉及到编码的地方一般都在字符到字节或者字节到字符的转换上,而需要这种转换的场景主要是在 I/O 的时候,这个 I/O 包括磁盘 I/O 和网络 I/O,关于网络 I/O 部分在后面将主要以 Web 应用为例介绍。下图是 Java 中处理 I/O 问题的接口:

     

    Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。值得注意的是如果你没有指定 Charset,将使用本地环境中的默认字符集,例如在中文环境中将使用 GBK 编码。

    写的情况也是类似,字符的父类是 Writer,字节的父类是 OutputStream,通过 OutputStreamWriter 转换字符到字节。如下图所示:

     

    同样 StreamEncoder 类负责将字符编码成字节,编码格式和默认编码规则与解码是一致的。

    如下面一段代码,实现了文件的读写功能:

    清单 1.I/O 涉及的编码示例

    String file = "c:/stream.txt"; 
     String charset = "UTF-8"; 
     // 写字符换转成字节流
     FileOutputStream outputStream = new FileOutputStream(file); 
     OutputStreamWriter writer = new OutputStreamWriter( 
     outputStream, charset); 
     try { 
        writer.write("这是要保存的中文字符"); 
     } finally { 
        writer.close(); 
     } 
     // 读取字节转换成字符
     FileInputStream inputStream = new FileInputStream(file); 
     InputStreamReader reader = new InputStreamReader( 
     inputStream, charset); 
     StringBuffer buffer = new StringBuffer(); 
     char[] buf = new char[64]; 
     int count = 0; 
     try { 
        while ((count = reader.read(buf)) != -1) { 
            buffer.append(buffer, 0, count); 
        } 
     } finally { 
        reader.close(); 
     }

    在我们的应用程序中涉及到 I/O 操作时只要注意指定统一的编解码 Charset 字符集,一般不会出现乱码问题,有些应用程序如果不注意指定字符编码,中文环境中取操作系统默认编码,如果编解码都在中文环境中,通常也没问题,但是还是强烈的不建议使用操作系统的默认编码,因为这样,你的应用程序的编码格式就和运行环境绑定起来了,在跨环境下很可能出现乱码问题。

    内存中操作中的编码

    在 Java 开发中除了 I/O 涉及到编码外,最常用的应该就是在内存中进行字符到字节的数据类型的转换,Java 中用 String 表示字符串,所以 String 类就提供转换到字节的方法,也支持将字节转换为字符串的构造函数。如下代码示例:

    String s = "这是一段中文字符串"; 
    byte[] b = s.getBytes("UTF-8");   //参数为编码类型
    String n = new String(b,"UTF-8"); //参数为编码类型

    另外一个是已经被被废弃的 ByteToCharConverter 和 CharToByteConverter 类,它们分别提供了 convertAll 方法可以实现 byte[] 和 char[] 的互转。如下代码所示:

    ByteToCharConverter charConverter = ByteToCharConverter.getConverter("UTF-8"); 
    char c[] = charConverter.convertAll(byteArray); 
    CharToByteConverter byteConverter = CharToByteConverter.getConverter("UTF-8"); 
    byte[] b = byteConverter.convertAll(c);

    这两个类已经被 Charset 类取代,Charset 提供 encode 与 decode 分别对应 char[] 到 byte[] 的编码和 byte[] 到 char[] 的解码。如下代码所示:

    Charset charset = Charset.forName("UTF-8"); 
    ByteBuffer byteBuffer = charset.encode(string); 
    CharBuffer charBuffer = charset.decode(byteBuffer);
    

    编码与解码都在一个类中完成,通过 forName 设置编解码字符集,这样更容易统一编码格式,比 ByteToCharConverter 和 CharToByteConverter 类更方便。

    Java 中还有一个 ByteBuffer 类,它提供一种 char 和 byte 之间的软转换,它们之间转换不需要编码与解码,只是把一个 16bit 的 char 格式,拆分成为 2 个 8bit 的 byte 表示,它们的实际值并没有被修改,仅仅是数据的类型做了转换。如下代码所以:

    ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024); 
    ByteBuffer byteBuffer = heapByteBuffer.putChar(c);

    以上这些提供字符和字节之间的相互转换只要我们设置编解码格式统一一般都不会出现问题。

    Java 中如何编解码

    前面介绍了几种常见的编码格式,这里将以实际例子介绍 Java 中如何实现编码及解码,下面我们以“I am 君山”这个字符串为例介绍 Java 中如何把它以 ISO-8859-1、GB2312、GBK、UTF-16、UTF-8 编码格式进行编码的。

    清单 2.String 编码

    public static void encode() { 
            String name = "I am 君山"; 
            toHex(name.toCharArray()); 
            try { 
                byte[] iso8859 = name.getBytes("ISO-8859-1"); 
                toHex(iso8859); 
                byte[] gb2312 = name.getBytes("GB2312"); 
                toHex(gb2312); 
                byte[] gbk = name.getBytes("GBK"); 
                toHex(gbk); 
                byte[] utf16 = name.getBytes("UTF-16"); 
                toHex(utf16); 
                byte[] utf8 = name.getBytes("UTF-8"); 
                toHex(utf8); 
            } catch (UnsupportedEncodingException e) { 
                e.printStackTrace(); 
            } 
     }

    我们把 name 字符串按照前面说的几种编码格式进行编码转化成 byte 数组,然后以 16 进制输出,我们先看一下 Java 是如何进行编码的。

    下面是 Java 中编码需要用到的类图

    图 1. Java 编码类图

     

    首先根据指定的 charsetName 通过 Charset.forName(charsetName) 设置 Charset 类,然后根据 Charset 创建 CharsetEncoder 对象,再调用 CharsetEncoder.encode 对字符串进行编码,不同的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面是 String. getBytes(charsetName) 编码过程的时序图

    图 2.Java 编码时序图

     

    从上图可以看出根据 charsetName 找到 Charset 类,然后根据这个字符集编码生成 CharsetEncoder,这个类是所有字符编码的父类,针对不同的字符编码集在其子类中定义了如何实现编码,有了 CharsetEncoder 对象后就可以调用 encode 方法去实现编码了。这个是 String.getBytes 编码方法,其它的如 StreamEncoder 中也是类似的方式。下面看看不同的字符集是如何将前面的字符串编码成 byte 数组的?

    如字符串“I am 君山”的 char 数组为 49 20 61 6d 20 541b 5c71,下面把它按照不同的编码格式转化成相应的字节。

    按照 ISO-8859-1 编码

    字符串“I am 君山”用 ISO-8859-1 编码,下面是编码结果:

     

    从上图看出 7 个 char 字符经过 ISO-8859-1 编码转变成 7 个 byte 数组,ISO-8859-1 是单字节编码,中文“君山”被转化成值是 3f 的 byte。3f 也就是“?”字符,所以经常会出现中文变成“?”很可能就是错误的使用了 ISO-8859-1 这个编码导致的。中文字符经过 ISO-8859-1 编码会丢失信息,通常我们称之为“黑洞”,它会把不认识的字符吸收掉。由于现在大部分基础的 Java 框架或系统默认的字符集编码都是 ISO-8859-1,所以很容易出现乱码问题,后面将会分析不同的乱码形式是怎么出现的。

    按照 GB2312 编码

    字符串“I am 君山”用 GB2312 编码,下面是编码结果:

     

    GB2312 对应的 Charset 是 sun.nio.cs.ext. EUC_CN 而对应的 CharsetDecoder 编码类是 sun.nio.cs.ext. DoubleByte,GB2312 字符集有一个 char 到 byte 的码表,不同的字符编码就是查这个码表找到与每个字符的对应的字节,然后拼装成 byte 数组。查表的规则如下:

     c2b[c2bIndex[char >> 8] + (char & 0xff)]

    如果查到的码位值大于 oxff 则是双字节,否则是单字节。双字节高 8 位作为第一个字节,低 8 位作为第二个字节,如下代码所示:

    复制代码

     1  if (bb > 0xff) {    // DoubleByte 
     2     if (dl - dp < 2) 
     3     return CoderResult.OVERFLOW; 
     4     da[dp++] = (byte) (bb >> 8); 
     5     da[dp++] = (byte) bb; 
     6  } else {            // SingleByte 
     7     if (dl - dp < 1) 
     8         return CoderResult.OVERFLOW; 
     9     da[dp++] = (byte) bb; 
    10  }

    复制代码

     

    从上图可以看出前 5 个字符经过编码后仍然是 5 个字节,而汉字被编码成双字节,在第一节中介绍到 GB2312 只支持 6763 个汉字,所以并不是所有汉字都能够用 GB2312 编码。

    按照 GBK 编码

    字符串“I am 君山”用 GBK 编码,下面是编码结果:

     

    你可能已经发现上图与 GB2312 编码的结果是一样的,没错 GBK 与 GB2312 编码结果是一样的,由此可以得出 GBK 编码是兼容 GB2312 编码的,它们的编码算法也是一样的。不同的是它们的码表长度不一样,GBK 包含的汉字字符更多。所以只要是经过 GB2312 编码的汉字都可以用 GBK 进行解码,反过来则不然。

    按照 UTF-16 编码

    字符串“I am 君山”用 UTF-16 编码,下面是编码结果:

     

    用 UTF-16 编码将 char 数组放大了一倍,单字节范围内的字符,在高位补 0 变成两个字节,中文字符也变成两个字节。从 UTF-16 编码规则来看,仅仅将字符的高位和地位进行拆分变成两个字节。特点是编码效率非常高,规则很简单,由于不同处理器对 2 字节处理方式不同,Big-endian(高位字节在前,低位字节在后)或 Little-endian(低位字节在前,高位字节在后)编码,所以在对一串字符串进行编码是需要指明到底是 Big-endian 还是 Little-endian,所以前面有两个字节用来保存 BYTE_ORDER_MARK 值,UTF-16 是用定长 16 位(2 字节)来表示的 UCS-2 或 Unicode 转换格式,通过代理对来访问 BMP 之外的字符编码。

    按照 UTF-8 编码

    字符串“I am 君山”用 UTF-8 编码,下面是编码结果:

     

    UTF-16 虽然编码效率很高,但是对单字节范围内字符也放大了一倍,这无形也浪费了存储空间,另外 UTF-16 采用顺序编码,不能对单个字符的编码值进行校验,如果中间的一个字符码值损坏,后面的所有码值都将受影响。而 UTF-8 这些问题都不存在,UTF-8 对单字节范围内字符仍然用一个字节表示,对汉字采用三个字节表示。它的编码规则如下:

    清单 3.UTF-8 编码代码片段

    private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst){ 
        char[] sa = src.array(); 
        int sp = src.arrayOffset() + src.position(); 
        int sl = src.arrayOffset() + src.limit(); 
        byte[] da = dst.array(); 
        int dp = dst.arrayOffset() + dst.position(); 
        int dl = dst.arrayOffset() + dst.limit(); 
        int dlASCII = dp + Math.min(sl - sp, dl - dp); 
        // ASCII only loop 
        while (dp < dlASCII && sa[sp] < '\u0080') 
            da[dp++] = (byte) sa[sp++]; 
        while (sp < sl) { 
            char c = sa[sp]; 
            if (c < 0x80) { 
                // Have at most seven bits 
                if (dp >= dl) 
                    return overflow(src, sp, dst, dp); 
                da[dp++] = (byte)c; 
            } else if (c < 0x800) { 
                // 2 bytes, 11 bits 
                if (dl - dp < 2) 
                    return overflow(src, sp, dst, dp); 
                da[dp++] = (byte)(0xc0 | (c >> 6)); 
                da[dp++] = (byte)(0x80 | (c & 0x3f)); 
            } else if (Character.isSurrogate(c)) { 
                // Have a surrogate pair 
                if (sgp == null) 
                    sgp = new Surrogate.Parser(); 
                int uc = sgp.parse(c, sa, sp, sl); 
                if (uc < 0) { 
                    updatePositions(src, sp, dst, dp); 
                    return sgp.error(); 
                } 
                if (dl - dp < 4) 
                    return overflow(src, sp, dst, dp); 
                da[dp++] = (byte)(0xf0 | ((uc >> 18))); 
                da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); 
                da[dp++] = (byte)(0x80 | ((uc >>  6) & 0x3f)); 
                da[dp++] = (byte)(0x80 | (uc & 0x3f)); 
                sp++;  // 2 chars 
            } else { 
                // 3 bytes, 16 bits 
                if (dl - dp < 3) 
                    return overflow(src, sp, dst, dp); 
                da[dp++] = (byte)(0xe0 | ((c >> 12))); 
                da[dp++] = (byte)(0x80 | ((c >>  6) & 0x3f)); 
                da[dp++] = (byte)(0x80 | (c & 0x3f)); 
            } 
            sp++; 
        } 
        updatePositions(src, sp, dst, dp); 
        return CoderResult.UNDERFLOW; 
     }

    UTF-8 编码与 GBK 和 GB2312 不同,不用查码表,所以在编码效率上 UTF-8 的效率会更好,所以在存储中文字符时 UTF-8 编码比较理想。

    几种编码格式的比较

    对中文字符后面四种编码格式都能处理,GB2312 与 GBK 编码规则类似,但是 GBK 范围更大,它能处理所有汉字字符,所以 GB2312 与 GBK 比较应该选择 GBK。UTF-16 与 UTF-8 都是处理 Unicode 编码,它们的编码规则不太相同,相对来说 UTF-16 编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间快速切换,如 Java 的内存编码就是采用 UTF-16 编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复,想比较而言 UTF-8 更适合网络传输,对 ASCII 字符采用单字节存储,另外单个字符损坏也不会影响后面其它字符,在编码效率上介于 GBK 和 UTF-16 之间,所以 UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。

    Java Web 涉及到的编码

    对于使用中文来说,有 I/O 的地方就会涉及到编码,前面已经提到了 I/O 操作会引起编码,而大部分 I/O 引起的乱码都是网络 I/O,因为现在几乎所有的应用程序都涉及到网络操作,而数据经过网络传输都是以字节为单位的,所以所有的数据都必须能够被序列化为字节。在 Java 中数据被序列化必须继承 Serializable 接口。

    这里有一个问题,你是否认真考虑过一段文本它的实际大小应该怎么计算,我曾经碰到过一个问题:就是要想办法压缩 Cookie 大小,减少网络传输量,当时有选择不同的压缩算法,发现压缩后字符数是减少了,但是并没有减少字节数。所谓的压缩只是将多个单字节字符通过编码转变成一个多字节字符。减少的是 String.length(),而并没有减少最终的字节数。例如将“ab”两个字符通过某种编码转变成一个奇怪的字符,虽然字符数从两个变成一个,但是如果采用 UTF-8 编码这个奇怪的字符最后经过编码可能又会变成三个或更多的字节。同样的道理比如整型数字 1234567 如果当成字符来存储,采用 UTF-8 来编码占用 7 个 byte,采用 UTF-16 编码将会占用 14 个 byte,但是把它当成 int 型数字来存储只需要 4 个 byte 来存储。所以看一段文本的大小,看字符本身的长度是没有意义的,即使是一样的字符采用不同的编码最终存储的大小也会不同,所以从字符到字节一定要看编码类型。

    另外一个问题,你是否考虑过,当我们在电脑中某个文本编辑器里输入某个汉字时,它到底是怎么表示的?我们知道,计算机里所有的信息都是以 01 表示的,那么一个汉字,它到底是多少个 0 和 1 呢?我们能够看到的汉字都是以字符形式出现的,例如在 Java 中“淘宝”两个字符,它在计算机中的数值 10 进制是 28120 和 23453,16 进制是 6bd8 和 5d9d,也就是这两个字符是由这两个数字唯一表示的。Java 中一个 char 是 16 个 bit 相当于两个字节,所以两个汉字用 char 表示在内存中占用相当于四个字节的空间。

    这两个问题搞清楚后,我们看一下 Java Web 中那些地方可能会存在编码转换?

    用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:

    图 3. 一次 HTTP 请求的编码示例

     

    如上图所示一次 HTTP 请求设计到很多地方需要编解码,它们编解码的规则是什么?下面将会重点阐述一下:

    URL 的编解码

    用户提交一个 URL,这个 URL 中可能存在中文,因此需要编码,如何对这个 URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个 URL:

    图 4.URL 的几个组成部分

     

    上图中以 Tomcat 作为 Servlet Engine 为例,它们分别对应到下面这些配置文件中:

    Port 对应在 Tomcat 的 <Connector port="8080"/> 中配置,而 Context Path 在 <Context path="/examples"/> 中配置,Servlet Path 在 Web 应用的 web.xml 中的

    <servlet-mapping> 
         <servlet-name>junshanExample</servlet-name> 
         <url-pattern>/servlets/servlet/*</url-pattern> 
    </servlet-mapping>

    <url-pattern> 中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端,这个将在后面再介绍。

    上图中 PathInfo 和 QueryString 出现了中文,当我们在浏览器中直接输入这个 URL 时,在浏览器端和服务端会如何编码和解析这个 URL 呢?为了验证浏览器是怎么编码 URL 的我们选择 FireFox 浏览器并通过 HTTPFox 插件观察我们请求的 URL 的实际的内容,以下是 URL:HTTP://localhost:8080/examples/servlets/servlet/ 君山 ?author= 君山在中文 FireFox3.6.12 的测试结果

    图 5. HTTPFox 的测试结果

    君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo 是 UTF-8 编码而 QueryString 是经过 GBK 编码,至于为什么会有“%”?查阅 URL 的编码规范 RFC3986 可知浏览器编码 URL 是将非 ASCII 字符按照某种编码格式编码成 16 进制数字然后将每个 16 进制表示的字节前加上“%”,所以最终的 URL 就成了上图的格式了。

    默认情况下中文 IE 最终的编码结果也是一样的,不过 IE 浏览器可以修改 URL 的编码格式在选项 -> 高级 -> 国际里面的发送 UTF-8 URL 选项可以取消。

    从上面测试结果可知浏览器对 PathInfo 和 QueryString 的编码是不一样的,不同浏览器对 PathInfo 也可能不一样,这就对服务器的解码造成很大的困难,下面我们以 Tomcat 为例看一下,Tomcat 接受到这个 URL 是如何解码的。

    解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的: 

    protected void convertURI(MessageBytes uri, Request request) 
     throws Exception { 
            ByteChunk bc = uri.getByteChunk(); 
            int length = bc.getLength(); 
            CharChunk cc = uri.getCharChunk(); 
            cc.allocate(length, -1); 
            String enc = connector.getURIEncoding(); 
            if (enc != null) { 
                B2CConverter conv = request.getURIConverter(); 
                try { 
                    if (conv == null) { 
                        conv = new B2CConverter(enc); 
                        request.setURIConverter(conv); 
                    } 
                } catch (IOException e) {...} 
                if (conv != null) { 
                    try { 
                        conv.convert(bc, cc, cc.getBuffer().length - 
     cc.getEnd()); 
                        uri.setChars(cc.getBuffer(), cc.getStart(), 
     cc.getLength()); 
                        return; 
                    } catch (IOException e) {...} 
                } 
            } 
            // Default encoding: fast conversion 
            byte[] bbuf = bc.getBuffer(); 
            char[] cbuf = cc.getBuffer(); 
            int start = bc.getStart(); 
            for (int i = 0; i < length; i++) { 
                cbuf[i] = (char) (bbuf[i + start] & 0xff); 
            } 
            uri.setChars(cbuf, 0, length); 
     }

    从上面的代码中可以知道对 URL 的 URI 部分进行解码的字符集是在 connector 的 <Connector URIEncoding=”UTF-8”/> 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。

    QueryString 又如何解析? GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值。对它们的解码是在 request.getParameter 方法第一次被调用时进行的。request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.Request 的 parseParameters 方法。这个方法将会对 GET 和 POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样。POST 表单的解码将在后面介绍,QueryString 的解码字符集是在哪定义的呢?它本身是通过 HTTP 的 Header 传到服务端的,并且也在 URL 中,是否和 URI 的解码字符集一样呢?从前面浏览器对 PathInfo 和 QueryString 的编码采取不同的编码格式不同可以猜测到解码字符集肯定也不会是一致的。的确是这样 QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 ISO-8859-1,要使用 ContentType 中定义的编码就要设置 connector 的 <Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true”/> 中的 useBodyEncodingForURI 设置为 true。这个配置项的名字有点让人产生混淆,它并不是对整个 URI 都采用 BodyEncoding 进行解码而仅仅是对 QueryString 使用 BodyEncoding 解码,这一点还要特别注意。

    从上面的 URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置 <Connector/> 中的 URIEncoding 和 useBodyEncodingForURI 两个参数。

    HTTP Header 的编解码

    当客户端发起一个 HTTP 请求除了上面的 URL 外还可能会在 Header 中传递其它参数如 Cookie、redirectPath 等,这些用户设置的值很可能也会存在编码问题,Tomcat 对它们又是怎么解码的呢?

    对 Header 中的项进行解码也是在调用 request.getHeader 是进行的,如果请求的 Header 项没有解码则调用 MessageBytes 的 toString 方法,这个方法将从 byte 到 char 的转化使用的默认编码也是 ISO-8859-1,而我们也不能设置 Header 的其它解码格式,所以如果你设置 Header 中有非 ASCII 字符解码肯定会有乱码。

    我们在添加 Header 时也是同样的道理,不要在 Header 中传递非 ASCII 字符,如果一定要传递的话,我们可以先将这些字符用 org.apache.catalina.util.URLEncoder 编码然后再添加到 Header 中,这样在浏览器到服务器的传递过程中就不会丢失信息了,如果我们要访问这些项时再按照相应的字符集解码就好了。

    POST 表单的编解码

    在前面提到了 POST 表单提交的参数的解码是在第一次调用 request.getParameter 发生的,POST 表单参数传递方式与 QueryString 不同,它是通过 HTTP 的 BODY 传递到服务端的。当我们在页面上点击 submit 按钮时浏览器首先将根据 ContentType 的 Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用 ContentType 中字符集进行解码。所以通过 POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过 request.setCharacterEncoding(charset) 来设置。

    另外针对 multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用 ContentType 定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉及到字符编码,而真正编码是在将文件内容添加到 parameters 中,如果用这个编码不能编码时将会用默认编码 ISO-8859-1 来编码。

    HTTP BODY 的编解码

    当用户请求的资源已经成功获取后,这些内容将通过 Response 返回给客户端浏览器,这个过程先要经过编码再到浏览器进行解码。这个过程的编解码字符集可以通过 response.setCharacterEncoding 来设置,它将会覆盖 request.getCharacterEncoding 的值,并且通过 Header 的 Content-Type 返回客户端,浏览器接受到返回的 socket 流时将通过 Content-Type 的 charset 来解码,如果返回的 HTTP Header 中 Content-Type 没有设置 charset,那么浏览器将根据 Html 的 <meta HTTP-equiv="Content-Type" content="text/html; charset=GBK" /> 中的 charset 来解码。如果也没有定义的话,那么浏览器将使用默认的编码来解码。

    其它需要编码的地方

    除了 URL 和参数编码问题外,在服务端还有很多地方可能存在编码,如可能需要读取 xml、velocity 模版引擎、JSP 或者从数据库读取数据等。

    xml 文件可以通过设置头来制定编码格式

    <?xml version="1.0" encoding="UTF-8"?>
    

    Velocity 模版设置编码格式:

     services.VelocityService.input.encoding=UTF-8

    JSP 设置编码格式:

    <%@page contentType="text/html; charset=UTF-8"%>
    

    访问数据库都是通过客户端 JDBC 驱动来完成,用 JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置 JDBC URL 来制定如 MySQL:url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"。 

     

    常见问题分析

    在了解了 Java Web 中可能需要编码的地方后,下面看一下,当我们碰到一些乱码时,应该怎么处理这些问题?出现乱码问题唯一的原因都是在 char 到 byte 或 byte 到 char 转换中编码和解码的字符集不一致导致的,由于往往一次操作涉及到多次编解码,所以出现乱码时很难查找到底是哪个环节出现了问题,下面就几种常见的现象进行分析。

    中文变成了看不懂的字符

    例如,字符串“淘!我喜欢!”变成了“Ì Ô £ ¡Î Ò Ï²»¶ £ ¡”编码过程如下图所示

     

    字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码,而且是一个汉字字符变成两个乱码字符。

    一个汉字变成一个问号

    例如,字符串“淘!我喜欢!”变成了“??????”编码过程如下图所示

     

    将中文和中文符号经过不支持中文的 ISO-8859-1 编码后,所有字符变成了“?”,这是因为用 ISO-8859-1 进行编解码时遇到不在码值范围内的字符时统一用 3f 表示,这也就是通常所说的“黑洞”,所有 ISO-8859-1 不认识的字符都变成了“?”。

    一个汉字变成两个问号

    例如,字符串“淘!我喜欢!”变成了“????????????”编码过程如下图所示

     

    这种情况比较复杂,中文经过多次编码,但是其中有一次编码或者解码不对仍然会出现中文字符变成“?”现象,出现这种情况要仔细查看中间的编码环节,找出出现编码错误的地方。

    一种不正常的正确编码

    还有一种情况是在我们通过 request.getParameter 获取参数值时,当我们直接调用

    String value = request.getParameter(name);

    会出现乱码,但是如果用下面的方式

    String value = String(request.getParameter(name).getBytes("ISO-8859-1"), "GBK"); 

    解析时取得的 value 会是正确的汉字字符,这种情况是怎么造成的呢?

    看下如所示:

     

    这种情况是这样的,ISO-8859-1 字符集的编码范围是 0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用 ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码,这种情况出现乱码时因为 Tomcat 的配置文件中 useBodyEncodingForURI 配置项没有设置为”true”,从而造成第一次解析式用 ISO-8859-1 来解析才造成乱码的。

    展开全文
  • 有关Unicode为什么会出现就不叙述了,Unicode是针对所有计算机的使用者定义一套统一的编码规范,这样计算机使用者就避免了编码转换的问题。 Unicode定义了所有符号的二进制形式,也就是符号如何在计算机内部存储的,...

           UNICODE是万能编码,包含了所有符号的编码,它规定了所有符号在计算机底层的二进制的表示顺序。有关Unicode为什么会出现就不叙述了,Unicode是针对所有计算机的使用者定义一套统一的编码规范,这样计算机使用者就避免了编码转换的问题。               Unicode定义了所有符号的二进制形式,也就是符号如何在计算机内部存储的,而且每个符号规定都必须使用两个字节来表示,也就是用16位二进制去代表一个符号,这样就导致了一个问题,英文编码的空间浪费,因为在ANSI中的符号都是一个字节来表示的,而使用了UNICODE编码就白白浪费了一个字节。也就代表着Unicode需要使用两倍的空间去存储相应的ANSI编码下的符号。虽然现在硬盘或者内存都很廉价,但是在网络传输中,这个问题就凸显出来了,你可以这样想想,本来1M的带宽在ANSI下可以代表1024*1024个字符,但是在Unicode下却只能代表1024*1024/2个字符。也就是1MB/s的带宽只能等价于512KB/s,这个很可怕啊。        所以为了解决符号在网络中传输的浪费问题,就出现了UTF-8编码,Unicode transfer format -8 ,后面的8代表是以8位二进制为单位来传输符号的,但是这样又导致了一个问题,虽然UTF-8可以使用一个字节来表示ANSI下的符号,但是对于其它类似汉语的符号,得需要两个字节来表示,所以计算机不知道如何去截取一个符号,也就是一个符号对应的二进制的截取开始位置和截取结束位置。所以为了解决Unicode下的ANSI符号的空间浪费和网络传输下如何截取字符的问题,UTF规定:

    • 如果一个符号只占一个字节,那么这个8位字节的第一位就为0。
    • 如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,
    • 然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。

    按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下8位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的,也就是Unicode下的16位减去UTF-8下的8位=8位,刚好差了一个字节的空间,所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符。这也就是为什么UTF-8需要使用三个字节去表示一个非ANSI字符的原因了!
     

    展开全文
  • 一个字节由几个二进制位组成

    千次阅读 2021-07-03 05:36:14
    事实上在计算机中一个字节由几个二进制位构成呢,小编各人带来一个字节由几个二进制位构成,一起来了解吧。一个字节由几个二进制位构成字节是以二进制盘算的,包含八位的二进制数。一个字节通常8位长,但是,一些...

    0fc874efad5d5c0c13b6860f38a26af4.png

    聊到几个,我们许多人都了解,有人问一个字节由几多个二进制位组成,还有朋友想问一个字节由几个二进制位构成,这到底怎么回事呢?事实上在计算机中一个字节由几个二进制位构成呢,小编为各人带来一个字节由几个二进制位构成,一起来了解吧。

    一个字节由几个二进制位构成

    字节是以二进制盘算的,包含八位的二进制数。

    一个字节通常8位长,但是,一些老型号计算机布局使用不同的长度。为了避免杂乱,在大多数国际文献中,利用词代替byte。

    在多数的计算机系统中,一个字节是一个8位长的数据单元,大多数的计算机用一个字节表现一个字符、数字或其他字符。一个字节也可以表现一系列二进制位。

    在一些计算机系统中,4 个字节代表一个字,这是计算机在实行指令时能够有效处理数据的单元。

    24492a82daa002d339a0788bd47a802b.png

    扩展资料

    位在计算机中,由于只有逻辑0和逻辑1的存在,因此许多东西、动作、数字都要表现为一串二进制的字码例如: 1001 0000 1101等等。

    此中每一个逻辑0或者1便是一个位。比方这个例子里的1000 1110共有八个位,它的英文名字叫(bit),是计算机中最根本的单位。

    字节由八个位构成的一个单元,也就是8个bit构成1个Byte。在计算机科学中,用于表现ASCII字符,便是运用字节来记载表示字母和一些符号,比方字符A便用 “0100 0001”来表现。

    而字节以上,便是字:16个位为一个字,它代表计算机处理指令或数据的二进制数位数,是计算机举行数据存储和数据处理的运算的单元。通常称16位是一个字,而32位呢,则是一个双字,64位是两个双字。

    一个字节由8个二进制位构成的。

    字节(Byte /bait/ n. [C])是计算机信息技能用于计量存储容量的一种计量单位,也表现一些计算机编程语言中的数据类型和语言字符。

    ASCII码:一个英文字母(包罗大小写)和数字、英文标点,占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单位,一般为8位二进制数,换算为十进制。最小值0,最大值255。如一个ASCII码就是一个字节。

    UTF-8编码:一个英文字符即是一个字节,一个中文(含繁体)即是三个字节。

    Unicode编码:一个英文即是两个字节,一个中文(含繁体)即是两个字节。

    标记:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的巨细,中文句号“。”占2个字节的巨细。

    相干单位

    B与bit

    数据存储是以“字节”(Byte)为单元,数据传输是以大多是以“位”(bit,又名“比特”)为单元,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)构成一个字节(Byte,简写为B),是最小一级的信息单元。

    易混观点辨析

    在计算机中,一串数码作为一个团体来处理或运算的,称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)。在存储器中,通常每个单位存储一个字,因此每个字都是可以寻址的。字的长度用位数来表现。

    在计算机的运算器、控制器中,通常都是以字为单元进行传送的。字出现在不同的地点其含义是不雷同。例如,送往控制器去的字是指令,而送往运算器去的字就是一个数。

    字长

    计算机的每个字所包罗的位数称为字长。根据计算机的差别,字长有固定的和可变的两种。固定字长,即字长度不论什么环境都是固定不变的;可变字长,则在肯定范围内,其长度是可变的。

    盘算的字长是指它一次可处理的二进制数字的数量。计算机处理数据的速率,自然和它一次能加工的位数以及举行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,纵然两台计算机的速度雷同,在相同的时间内,前者能做的事情是后者的两倍。

    一般地,大型计算机的字长为32-64位,小型计算机为12-32位,而微型计算机为4-16位。字长是权衡计算机性能的一个重要因素。

    字节

    字节是指一小组相邻的二进制数码。通常是8位作为一个字节。它是组成信息的一个小单位,并作为一个团体来参加操作,比字小,是组成字的单位。

    在微型计算机中,通常用几多字节来表示存储器的存储容量。

    比方,在C++的数据类型表示中,通常char为1个字节,int为4个字节,double为8个字节。

    明白编码的关键,是要把字符的观点和字节的概念明白准确。这两个概念轻易混淆,我们在此做一下区分:

    观点描述 举例

    字符人们利用的记号,抽象意义上的一个标记。 '1', '中', 'a', '$', '¥' ……

    字节计算机中存储数据的单位,一个8位的二进制数,是一个很详细的存储空间。

    字符串

    在内存中,假如“字符”是以ANSI编码情势存在的,一个字符大概使用一个字节或多个字节来表现,那么我们称这种字符串为ANSI字符串大概多字节字符串。如,"中文123" (占8字节,包罗一个隐藏的\0)。

    数据传输是以2进制表现的,所以1KB不即是1000B。

    1KB=1024B;1MB=1024KB=1024×1024B。此中1024=210。

    1B(byte,字节)= 8 bit;

    1KB(Kibibyte,千字节)=1024B= 2^10 B;

    1MB(Mebibyte,兆字节,百万字节,简称“兆”)=1024KB= 2^20 B;

    1GB(Gigabyte,吉字节,十亿字节,又称“千兆”)=1024MB= 2^30 B;

    1TB(Terabyte,万亿字节,太字节)=1024GB= 2^40 B;

    1PB(Petabyte,千万亿字节,拍字节)=1024TB= 2^50 B;

    1EB(Exabyte,百亿亿字节,艾字节)=1024PB= 2^60 B;

    1ZB(Zettabyte,十万亿亿字节,泽字节)= 1024EB= 2^70 B;

    1YB(Yottabyte,一亿亿亿字节,尧字节)= 1024ZB= 2^80 B;

    1BB(Brontobyte,一千亿亿亿字节)= 1024YB= 2^90 B;

    1NB(NonaByte,一百万亿亿亿字节) = 1024 BB = 2^100 B;

    1DB(DoggaByte,十亿亿亿亿字节) = 1024 NB = 2^110 B;

    一个“字节”由几多个“二进制位”组成?

    在计算机中,一个“字节”由8个“二进制位”构成。

    字节:由8个二进制位组成1个字节。即1Byte=8Bit.

    字:差别的计算机系统一次可以处理的字长是差别的,16位计算机的字长就是16位,32位计算机的字长就是32位。

    但是早在十六位机期间,人们将字长定义为16位。厥后出现32位机,有人便将32位称之为双字。等出现64位机之后,又将其称之为四字。这成为一种约定俗成的叫法,与早先对字长的界说无关了。

    计算机存储器中,一个字节由几多位二进制位组成

    一个字节(BYTE)由8位(BIT)二进制位构成

    一字节是几个二进制位?

    一字节是八个二进制位。

    字节(Byte /bait/ n. [C])是计算机信息技能用于计量存储容量的一种计量单位,也表现一些计算机编程语言中的数据类型和语言字符。

    数据存储是以“字节”(Byte)为单元,数据传输是以大多是以“位”(bit,又名“比特”)为单元,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)构成一个字节(Byte,简写为B),是最小一级的信息单元。

    一个字由几多个二进制位组成

    1字=2字节

    1字节由8位二进制构成

    以是1字=2*8=16位

    计算机中的一个汉字码由两个字节(16bits) 构成.

    至于数值即是几,要看编码类型和要领.

    比方,国标GB2312,国标GBK,大五码,unicode 小端码,unicode 大端码,HZ码

    另有他们的传输形式,html 十六进制码,html 十进制码,UTF-8, UTF-16 等.

    计算机中的一个汉字字体的存放很庞大,字体有向量情势,点阵形式,占的存放单位大小变化很大.二进制数值内容与字的刻痕陈迹有关,千变万化.

    一个字节包罗几个二进制数?

    字节:由8个二进制位组成1个字节,即1Byte=8Bit

    c24b806966361c3b76c755a3757bacd1.png

    拓展资料

    字节(Byte /bait/ n. [C])是计算机信息技能用于计量存储容量的一种计量单位,通常环境下一字节等于有八位,也表现一些计算机编程语言中的数据类型和语言字符。

    字符与字节

    ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单位,一般为8位二进制数,换算为十进制。最小值-128,最大值127。如一个ASCII码就是一个字节。

    UTF-8编码:一个英文字符即是一个字节,一个中文(含繁体)即是三个字节。中文标点占三个字节,英文标点占一个字节

    Unicode编码:一个英文即是两个字节,一个中文(含繁体)即是两个字节。中文标点占两个字节,英文标点占两个字节

    差别数量级间

    国际单位制(SI )

    1KB=1000B;1MB=1000KB=1000×1000B。此中1000=103。

    1B(byte,字节)= 8 bit;

    1KB(kilobyte,千字节)=1000B= 10^3 B;

    1MB(Megabyte,兆字节,百万字节,简称“兆”)=1024KB= 10^6 B;

    1GB(Gigabyte,吉字节,十亿字节,又称“千兆”)=1024MB= 10^9 B;

    1TB(Terabyte,万亿字节,太字节)=1024GB= 10^12 B;

    1PB(Petabyte,千万亿字节,拍字节)=1000TB= 10^15 B;

    1EB(Exabyte,百亿亿字节,艾字节)=1000PB= 10^18 B;

    1ZB(Zettabyte,十万亿亿字节,泽字节)= 1000EB= 10^21 B;

    1YB(Yottabyte,一亿亿亿字节,尧字节)= 1000ZB= 10^24 B;

    1BB(Brontobyte,一千亿亿亿字节)= 1000YB= 10^27 B;

    1NB(NonaByte,一百万亿亿亿字节) = 1000 BB = 10^30B;

    1DB(DoggaByte,十亿亿亿亿字节) = 1000 NB = 10^33 B。

    在计算机中一个字节由几多位二进制位组成

    一个字节(BYTE)由8位(BIT)二进制位构成。

    二进制是计算技术中遍及采用的一种数制。二进制数据是用0和1两个数码来表现的数。它的基数为2,进位规矩是“逢二进一”,借位规矩是“借一当二”,由18世纪德国数理哲学大家莱布尼兹发现。当前的计算机系统利用的基本上是二进制系统,数据在计算机中重要是以补码的情势存储的。计算机中的二进制则是一个非常微小的开关,用“开”来表现1,“关”来表示0。

    20世纪被称作第三次科技革命的紧张标志之一的计算机的发明与应用,由于数字计算机只能识别和处理由‘0’.‘1’符号串构成的代码。其运算模式正是二进制。19世纪爱尔兰逻辑学家乔治布尔对逻辑命题的思索过程转化为对符号"0''.''1''的某种代数演算,二进制是逢2进位的进位制。0、1是根本算符。因为它只使用0、1两个数字标记,非常简单方便,易于用电子方法实现。

    计算机存储器中,一个字节是由几多为二进制位组成

    一个字节由8位二进制位构成。 因为数据存储是以“字节”(Byte)为单元,数据传输是以大多是以“位”(bit,又名“比特”)为单元,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)构成一个字节(Byte,简写为B),是最小一级的信息单元。Byte数据类型(字节型)用一个字节(Byte)储存,可区别256个数字,取值范畴:0到255。 Byte是从0-255的无标记类型。

    百度宁静验证

    展开全文
  • 原来以为int(11)是指11个字节,int(10)就是10个字节。我错了。http://zhidao.baidu.com/link?url=puYWaGBQNKNHgffO5kdvXshF3KmX8OuB4Mor3HXapbNHa8m1CdlF8PJTqVuKa1eKcEd6Bv2NKUr3I-KJr5-7ISLhBsmf17L...
  • mysql里中文多少个字节

    千次阅读 2021-02-05 04:51:24
    在mysql中,一个中文汉字所字节数与编码格式有关:如果是GBK编码,则一个中文汉字2个字节;如果是UTF8编码,则一个中文汉字3个字节,而英文字母1字节。mysql里中文多少个字节?1. 一个中文汉字多少...
  • Python中的整型多少个字节

    千次阅读 2020-11-21 03:32:57
    说到计算机中的整型,相信很多人都会联想到32位整型(或者int),是程序员日常生活中用的最多的一种类型。32位整型顾名思义,占用32个位也就是4个字节,取值范围−2,147,483,648...但是Python中的一个整型到底占用多...
  • 一:1个汉字 = 1个字 = 1个字符二:1个字符 = 1个字节 = 8bit(ACSII码下)三...今天测试了下mysql发现不对了可以看到第一个的长度确实是15,但是第二个为什么是5?在网上找到资料:char_length计算的是字符长度,而l...
  • Java中int为什么占个字节

    千次阅读 2021-08-25 18:53:57
    简单了解计算机为什么要采用二进制表示信息: 因为计算机作为种电子计算机工具,是由大量的电子器件组成的,在这些电子器件中,电路的通断,电位的高低,用两个数字符号“1”和“0”分别表示容易实现,同时二进制...
  • Java 为什么占个字节的char

    千次阅读 2020-11-19 11:24:55
    Java 为什么占两个字节的char Java语言内部存储采用的是Unicode编码,Unicode编码中的每个字符两个字节,中文也是两个字节,所以,Java中的字符可以存储一个中文汉字。 1、而在不同的编码方式中,中文所的...
  • java中字符串个字节

    千次阅读 2021-02-12 20:00:12
    首先,charJava的基本类型,基本类型所占的字节数是固定的,如int占4字节,double占8字节,这可以使得Java在不同的平台上所占...在String中,一个英文字符占1个字节,而中文字符根据编码的不同所占字节数也不同。...
  • 一个指针在32位操作系统上,4个字节一个指针在64位操作系统上,8个字节但是,编译器为了兼容32位操作系统和64位操作系统,所以指针都是4个字节长度为什么呢?在计算机中,CPU不能直接与硬盘进行数据交换,CPU...
  • 一个指针几个字节?原理是什么呢?

    万次阅读 多人点赞 2019-04-01 14:44:08
    一个指针几个字节的问题,感觉会C语言的同学都知道。但是在面试过程中,面了几个同学,不是答忘记了,就是两个、四个的瞎蒙。。。 那么,一个指针到底几个字节呢? 其实,这个问题很简单,稍微上网一搜,你就...
  • float个字节

    千次阅读 2021-03-06 18:54:00
    float类型占用4字节内存,表示小数,数据范围在【-2^128 ~ 2^128】【-3.40E+38 ~ +3.40E+38 】之间;float数据类型用于存储单精度浮点数或双精度浮点数。float类型占用4字节内存,表示小数,数据范围在-2^128 ~ 2^...
  • 一个汉字到底是多少个字节

    千次阅读 2021-04-14 00:16:09
    一个二进制数字序列,在计算机中作为一个数字单元,一般8位二进制数,换算十进制。最小值0,最大值255。如一个ASCII码就是一个字节。 UTF-8编码: 一个英文字符等于一个字节一个中文(含繁体)等于三个字节...
  • int个字节(c语言)?

    千次阅读 2021-02-09 03:26:41
    int在16位编译器中个字节,int在32位编译器中个字节。“int”是整数类型的类型名,长整型的类型名“long int”,可简写“long”,“int”和“long”都是关键字。int在16位编译器中个字节,int在32位...
  • 一个汉字在数据库几个字节

    千次阅读 2021-01-19 04:42:25
    项目中oracle10g数据库表字段...UTF-8字符集,一个汉字三个字节,gbk字符集,一个汉字两个字节,比如varchar(10)类型的字段,UTF-8的汉字,只能存3个,gbk字符集的汉字却能存5个。所以在程序中根据表字段varch...
  • 一个字符几个字节

    万次阅读 2019-01-02 08:49:46
    一个二进制数字序列,在计算机中作为一个数字单元,一般8位二进制数,换算十进制。最小值0,最大值255。如一个ASCII码就是一个字节。 UTF-8编码:  一个英文字符等于一个字节一个中文(含繁体)等于三个字节...
  • oracle数据库一个汉字几个字节

    千次阅读 2019-10-17 09:39:59
    ***和数据库的编码有关系*** 1、数据库编码查询sql: 1)、SELECT value$ FROM sys.props$ WHERE name = 'NLS_... ZHS16GBK:一个汉字占用2个字节 AL32UTF8:一个汉字占用3个字节 2)、select userenv...
  • 一个汉字到底几个字节

    千次阅读 2020-08-12 16:31:47
    总结:这和编码有关,UTF8一个汉字3个字节,GBK一个汉字2个字节。 当我上第一节计算机课的时候,我的电脑老师跟我说,一个英文字符是1个字节一个中文是2个字节。这么多年来,我对此一直坚信不移,相信很多人也...
  • 表示定义一个短整型的变量i。依据程序编译器的不同short定义的字节数不同,标准定义short短整型变量不得低于16位,即两个字节,编译器头文件夹里面的limits.h定义了short能表示的大小:SHRT_MIN~SHRT_MAX,在32位...
  • 一个16进制的数多少字节?(半个字节

    万次阅读 多人点赞 2021-01-03 19:20:33
    1、1个字节是8位,二进制8位:xxxxxxxx 范围从00000000-11111111,表示0到255。...1字节等于8位,可以代表256个数字(在编程中可以通过这些数字作为判断),int类型一般4字节,即32位。 一个十六
  • 因为计算机是美国人发明的,美国人制定的计算机编码是ASCII码,定义8(bit)位二进制数为1byte(字节),为什么是8位,因为2的8次方为256,英文的字母,控制符,符号可以用256编码内全部包含。然后后来计算机应用...
  • javascript中一个字符几个字节

    千次阅读 2019-01-23 10:23:01
    一般来说英文是1,中文是两。但是会根据编码方式不同而不同。以下是搬运: 英文字母和中文汉字在不同字符集编码下的字节数 英文字母: 字节数 : 1;编码:GB2312 字节数 : 1;编码:GBK 字节数 : 1;编码:GB18030 ...
  • UTF-8编码个字节?

    千次阅读 2021-12-08 09:44:36
    占2个字节的:带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文...一个utf8数字占1个字节 一个utf8英文字母占1个字节 少数是汉字每个占用3个字节,多数占用4个字节。 占用3个字节的范围 U+2E80 -
  • 为什么一个字节是八个bit

    万次阅读 多人点赞 2019-06-10 19:10:29
    但是为什么一个字节是八个bit呢 bit bit是计算机存储数据的最小单位,只有0和1两种值。对于计算机来说,数据只有0和1两种值。 ascii码 ascii码是计算机一开始使用的编码协议,主要用于对应需要显示的字符和 ”0与1...
  • 相关题目与解析ASCII码是美国国际信息交换码,计算机中用()个字节存放一个ASCII码A.1B.2C.3D.4一个字符在计算机内存储占用()字节。A.8B.4C.2D.1计算机中用一个字节来存放一个ASCII码字符,用两个字节来存放一个汉字的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 134,700
精华内容 53,880
关键字:

一个数字为什么占一个字节