Java中乱码问题

1 篇文章 0 订阅

1.为什么会出现字符编码

(此字符编码不是 字符集中的字符编码)因为 计算机中保存的数据都是二进制数据,这只有计算机能看懂,而我们人类是看不懂的,所以先要将人类语言 按照规则 翻译成计算机的语言,然后人类读的时候再将 计算机语言按照规则翻译成人类语言

2.字符集与字符编码

字符集:就是字符按照一定规则转化成二进制进行保存,因为大家使用的规则不一样,使用字符集就闪亮登场了
字符编码:虽然字符集已经将字符映射到了对应的二进制,但是不一定直接将它存贮,有时候,我们按照一定的规则,将它再次处理,以更加适应计算机存储、网络传输的需要。

常见的编码:

  • ASCII
    总共128个用1个字节的低7位表示,0~31是控制字符如换行,回车,删除等,32 ~126是打印字符 (既表示了字符集,也代表一种字符编码)

  • ISO-8859-1 (兼容ASCII编码) 数字和西欧字母
    ASCII 只能表示128个字符,太少了,于是拓展了,其中ISO-8859-1涵盖了大多数西欧语言字符,单字节编码 ,总共表示256个字符(既表示了字符集,也代表一种字符编码)

  • GB2312 (兼容ASCII)
    双字节编码,编码范围A1~A9 是符号区,A1到A9是符号区,总共682符号;B0 ~ F7 汉字区,包含6763个汉字

  • GBK(兼容GB2312)
    GBK是GB2312的拓展

  • UNICODE(兼容ISO-8859-1) 统一码 万国码

1.1UNICODE 与UTF

Unicode只定义了字符集,没有定义字符编码。

1.1.1 为什么Unicode不像ASCII码一样既是字符集又是字符编码呢?

例如 "哈"这个字符 对应Unicode字符集中的0x54c8,对应二进制为101 0100 1100 1000
一个15位,所有至少需要2个字节来表示,但是Unicode表很大啊,可能用3个字节,4个字节但是我程序获取的时候不知道几个字节代表一个字符,如果用最大字节数,代表一个字符,不够补0,这样会造成造成极大的浪费

因为UNICODE要存放很多数据,完全按照UNICODE来存储数据会造成极大的浪费

所以出现了很多对UNICODE减肥的编码

  • UTF-8
  • UTF-16
  • UTF-32

3.Java中的乱码问题

3.1 为什么会出现乱码问题

因为编码和解码使用的规则不一样(不兼容)

3.2 Java中的编码解码

什么时候需要编码,在数据传输的时候

3.2.1 IO

io按传输类型分为 字节流和字符流
字节流 你保存的是什么字节你读取的就是什么字节 转字符串的时候注意一下编码

new String(bytes,"ISO-8859-1");

字符流是字节流+字符编码

3.2.1.1 InputStreamWrite和OutputStreamWrite

FileWriter 是OutputStreamWrite的子类 相当于

InputStreamReader input =new InputStreamReader(new FileInputStream(file),"默认编码");

下面会提及默认编码

字符转字节

        System.out.println(System.getProperty("file.encoding"));
        System.out.println(Charset.defaultCharset());
 String fileName = "D:/study/test/temporary.txt";
        try(FileWriter fileWriter = new FileWriter(fileName);) {
            String s = "我真帅";
            fileWriter.write(s);
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

输出

UTF-16
UTF-16

以InputStreamWrite为栗
字节转字符

  try (  InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("D:/study/test/temporary.txt"),"UTF-16");){
                   char[] chars=new char[20];
           inputStreamReader.read(chars);
            System.out.println(new String(chars));
        } catch (IOException e) {
            e.printStackTrace();
        }

输出

我真帅 

这时候我们如果用不同的编码(同时不兼容)去读取试一试

        try (  InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("D:/study/test/temporary.txt"),"UTF-8");){
                   char[] chars=new char[20];
           inputStreamReader.read(chars);
            System.out.println(new String(chars));
        } catch (IOException e) {
            e.printStackTrace();
        }

输出

��bw^    

我的脸在你看来很帅,有的人看就马赛克了

在这里插入图片描述

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

建议写用什么字符编码读就用什么字符编码

3.2.2 字符串

String是由一个个char组成的 ,java中以UTF-16作为内存的字符存储格式,UTF-16用两个字节来表示Unicode的转化格式,是定长的表述方式

        String iso="wo hao shuai";
        //编码
        byte[] bytes = iso.getBytes("ISO-8859-1");
         //解码
        String string = new String(bytes,"ISO-8859-1");
        System.out.println(string);

输出

wo hao shuai

如果你的传输方使用了ISO_8859_1而你用的不是这个且不兼容就会出现乱码

        String iso="wo hao shuai";
        //编码
        byte[] bytes = iso.getBytes("ISO-8859-1");
         //解码
        String string = new String(bytes,"UTF-16");
        System.out.println(string);

输出

睯⁨慯⁳桵慩

String.getBytes(charsetName)时序图在这里插入图片描述

3.2.3 默认字符集

获取file.encoding jvm系统编码,如果jvm启动的时候不指定file.encoding 默认为当前操作系统编码,如果为空默认字符集为UTF-8

public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                String csn = AccessController.doPrivileged(
                    new GetPropertyAction("file.encoding"));
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }

3.2.4 Web 涉及到的编码

在这里插入图片描述

3.2.4.1 URL编解码
https://www.baidu.com/s?ie=utf-8&tn=02003390_21_hao_pg&wd=%E4%BB%8A%E5%A4%A9%E5%8F%88%E5%B8%85%E4%BA%86

一段URL中很有可能出现中文参数,那么这段URL那么是按照什么规则来编码和解码的呢?
浏览器编码URL是将非ASCII字符按照某种编码格式编码成16进制然后将每个16进制表示的字节前加上%

关于客户端浏览器与服务端先挖个坑,以后填
-2020年8月6日 留-

3.2.5 总结

使用统一的编码,避免使用软件或者操作系统默认的编码格式

参考:
深入分析Java web 技术内幕

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值