3.1编码基础知识

前言

此章节知识轻微硬核,算是一个学习报告,不算是一个自我总结;

大多数代码为照搬,但是个人对整个结构有所整理,也请放心食用。

3.x都为《深入分析JavaWeb技术内幕》中第三章整章的学习报告及整理。

基础知识

为什么要编码

  • 计算机中存储信息的最小单元是一个字节即 8 个 bit,所以能表示的字符范围是 0~255 个

  • 人类要表示的符号太多,无法用一个字节来完全表示

  • 要解决这个矛盾必须需要一个新的数据结构 char(我们暂时这么叫它),所以从 char 到 byte 必须编码,来表示我们想要的符号or文字。

(其实我更推荐看完整个第三章,你自己问问自己为什么要编码)
眼中有码,心中无码(狗头保命)

常见编码

计算中提拱了多种翻译方式,常见的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它们都可以被看作为字典,它们规定了转化的规则,按照这个规则就可以让计算机正确的表示我们的字符。

  • ASCII 码(CS相关专业的一定不陌生)

    学过计算机的人都知道 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 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节

所以说UTF-16不是UTF-8的升级版哦,这是两个东西

Java中需要使用编解码的场景

在I/O中

​ 涉及到编码的地方一般都在字符到字节或者字节到字符的转换上,而需要这种转换的场景主要是在 I/O 的时候,这个 I/O 包括磁盘 I/O 和网络 I/O(网络 I/O 部分在后面介绍)。下图是 Java 中处理 I/O 问题的接口:

  • 对于读:在这里插入图片描述

InputStreamReader 类是桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式

​ 如果你没有指定 Charset,将使用本地环境中的默认字符集。(例如在中文环境中将使用 GBK 编码)

  • 对于写:
    在这里插入图片描述

    写和读类似,OutputStreamWriter 转换字符到字节,StreamEncoder 类负责将字符编码成字节(I/O知识不够去前面博客补)

    下列为读&写的示例代码(出自《深入分析JavaWeb技术内幕》)

            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编码与解码都在一个类中完成,通过 forName() 设置编解码字符集,这样更容易统一编码格式:

    Charset charset = Charset.forName("UTF-8");
    ByteBuffer byteBuffer = charset.encode(string);
    CharBuffer charBuffer = charset.decode(byteBuffer);
    
  • 还有一个 ByteBuffer 类,它提供一种 char 和 byte 之间的软转换,它们之间转换不需要编码与解码,只是把一个 16bit 的 char 格式,拆分成为 2 个 8bit 的 byte 表示,它们的实际值并没有被修改,仅仅是数据的类型做了转换。(就感觉水的一比)

    ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024); //分配大小1024
    ByteBuffer byteBuffer = heapByteBuffer.putChar(c);//感觉没有什么卵用
    
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值