2025年String 字符编码 乱码原理讲解 java

String 字符编码 乱码原理讲解 javastring 中文乱码 也许大家都经历过 解决方案网上一搜一大堆 有用的无用的 挨个试一下总会有能用的 但是 我们不应该只看中问题的解决方案 更看重的应该是为什么会这么解决 问题产生的原因是什么 否则 我们永远不会进步 进入正题 先说一下基本知识

大家好,我是讯享网,很高兴认识大家。

string 中文乱码 ,也许大家都经历过,解决方案网上一搜一大堆。有用的无用的,挨个试一下总会有能用的。但是,我们不应该只看中问题的解决方案,更看重的应该是为什么会这么解决,问题产生的原因是什么? 否则,我们永远不会进步。

进入正题,先说一下基本知识,关于字符编码的。众所周知,世界上面有很多的语言,特别多。为了在计算机中存储语言,编码格式就诞生了。

编码格式

通俗的讲呢,编码格式就是 用指定的计算机存储 对应 语言单词 字母之类的。就像ASCII表那样。

乱码的原因

编码格式和解码格式不一样。举个例子,我们约定 一个规则,存数字的时候 ,要先加3,然后再存起来。 读取数字的时候,要先减3 ,才是原本的值。
比如要存5,那么要先加3 ,变成8 ,再存入。 读取时候,取出来的8,要先减3,才能得到5。
我们要按照相同的规则 存取,才能得到正确的值。string 在文件里面底层保存形式是二进制,高级形式就是byte[]byte[]数组里面的内容可以按照不同的编码格式存放。我们读取字符串的时候,也可以按照不同的解码格式存放。 这样就造成了 乱码。

如何保证不乱码?

从始至终,都用同一种字符格式

方法

认识两个方法 ,下面要用的。

//将调用的string 用给定的编码 转成 字节数组 //charsetName 编码格式  public byte[] getBytes(String charsetName) 

讯享网
讯享网//将给定的字节数组,用给定的解码格式 ,生成一个Sting public String(byte bytes[], String charsetName) 

这两个方法里面的charsetName参数,其实并没有编码解码之分,只是想让读者便于理解

例子解读

 String path = "D:/one.txt"; File file = new File(path); if (!file.exists()){ 
    file.createNewFile(); } FileOutputStream outputStream = new FileOutputStream(file); //原始内容 String content = "中文englis》》》》";//原始字符串 这时候 与编码格式无关 ,它可以转成任意格式的字节数组 byte[] utfBytes = content.getBytes("UTF-8");//用UTF-8编码 把content编码成 utf格式字节数组 byte[] gbkBytes = content.getBytes("GBK");//用GBK编码 把content编码成 gbk格式字节数组 byte[] isoBytes = content.getBytes("ISO-8859-1");//用ISO-8859-1编码 把content编码成 iso-8859-1格式字节数组 byte[] gb2312Bytes = content.getBytes("GB2312");//用GB2312编码 把content编码成 gb2312格式字节数组 outputStream.write(gb2312Bytes);//将gb2312Bytes 数组写入流,这样 在txt文件中 文字是以 GB2312编码格式存储的 outputStream.flush(); outputStream.close(); 

上面呢,我们创建了一个txt文件,并且 将"中文englis》》》》"用gb2312编码格式存储了。以后我们从one.txt里面读取这段话的数组,也是gb2312格式的
在这里插入图片描述
讯享网

读取文件

讯享网 //以后我们从one.txt文件里面读取流,读到的也是用gb2312格式字节数组 //这里省略从one.txt文件里面读取流 变成字节数组的过程, 读取出来的数组 是和gb2312Bytes 是一样的 //只要字节数组编码格式,和解码格式 是相同的就不会中文乱码 // gb2312Bytes 用gb2312解码 String gb2312ToGb2312 = new String(gb2312Bytes, "GB2312"); System.out.println("gb2312ToGb2312 字节数组编码格式和解码格式相同 "+gb2312ToGb2312); //如果 字节数组编码格式,和解码格式 不同 就会产生中文乱码 //gb2312编码 ISO-8859-1解码 ——》乱码 String gbk312ToISO = new String(gb2312Bytes, "ISO-8859-1");//gb2312格式字节数组 却用ISO-8859-1的解码 System.out.println("gb2312ToGbk 字节数组编码格式和解码格式不同 "+gbk2312ToISO);//输出乱码 

从上面代码可以看出,只要字节数组编码和解码格式相同,就不会产生乱码

字符串已经乱码(用不一样的解码 构造String)后,如何将乱码字符修复。而不是用原数组 重新构造一个String?

接着上面的代码

 //下面 将乱码修复 byte[] temp = gb2312ToISO.getBytes("ISO-8859-1");//因为上面是用ISO-8859-1解码的,所以 用ISO-8859-1编码 逆转到 原先数组 也就是gb2312Bytes数组 System.out.println("乱码字符 ,编码后的数组,和原先gb2312数组比较 "+isEqual(temp,gb2312Bytes));//比较 乱码字符 重新编码后的数组 和 原先的gb2312Bytes数组 System.out.println("乱码字符 ,编码后的数组,和原先iso-8859-1数组比较 "+isEqual(temp,isoBytes));//比较 乱码字符 重新编码后的数组 和 原先的gb2312Bytes数组 System.out.println("temp 数组 解码 "+new String(temp,"GB2312"));//再用GB2312解码 

在这里插入图片描述
从上面代码可以看见,首先调用getBytes()方法,将gb2312ToISO 再重新用ISO-5589-1编码 转成字节数组。再用gb2312解码重新构造 生成一个的字符串。 可以看见 乱码被修复了。

UTF-8格式的特殊性

讯享网 //注意 如果 你使用UTF-8解码, 意味着 string 只能被 重新编码成 utf-8格式的原数组(解码 编码后 数组相等),不能被重新编码其他格式的数组(解码 编码后 数组不相等) String gb2312ToUTF= new String(gb2312Bytes, "UTF-8");//字节数组编码 和第二个参数不一致 乱码 System.out.println("gb2312ToUTF 编码gb2312 解码UTF-8 "+gb2312ToUTF);//乱码 //按照上面的方法修复 byte[] gb2312ToUTFBytes = gb2312ToUTF.getBytes("UTF-8");//乱码 重新编码 System.out.println("gbk2312原数组, 和先解码UTF-8,再编码 返回的数组比较"+isEqual(gb2312Bytes,gb2312ToUTFBytes)); System.out.println("utf原数组, 和先解码UTF-8,再编码 返回的数组比较"+isEqual(utfBytes,gb2312ToUTFBytes)); String one = new String(gb2312ToUTFBytes, "UTF-8"); System.out.println("one 此时乱码字符已经不能修复 "+one); System.out.println(""); //用utf编译的数组,构造GBK格式的字符串 String o = new String(utfBytes, "GBK"); System.out.println(o); //识图修复 System.out.println(new String(o.getBytes("GBK"),"UTF-8")); 

在这里插入图片描述
我们首先,构造一个String字符串,原数组是用gb2312编码的,解码是用UTF-8。不出意外,乱码了。当我们按照上面的修复方法 修复时候。发现 这个乱码的String再也不能被修复了。这就是UTF-8的特殊性你使用UTF-8解码, 意味着 string 只能被 重新编码成 utf-8格式的原数组(解码 编码后 数组相等),不能被重新编码其他格式的数组(解码 编码后 数组不相等)

总结UTF-8

只要在解码构造String的过程中,字节数组和解码格式 任意一个是UTF-8。那么,生成的乱码字符串将不能被修复。这时候只能 第一次构造String时候, 编码格式和解码格式相等,才能不乱码

BufferedReader和RandomAccessFile的注意事项

 System.out.println("\r\nBufferedReader"); //如果在构造 InputStreamReader的时候 没有指定 解码格式 charsetName,那么会默认的使用jvm的字符编码(在IDEA中),cmd窗口会使用操作系统的字符编码。 //现在默认的 一般都是 UTF-8 InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(path)); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String read = null; while ((read=bufferedReader.readLine())!=null){ 
    System.out.println("读取出来的乱码 "+read); //运用修复方法 read = new String(read.getBytes(), "GB2312"); System.out.println("依旧乱码 不可修复 "+read); } bufferedReader.close(); inputStreamReader.close(); System.out.println("\r\n RandomAccessFile"); //RandomAccessFile 是用ISO-8859-1 读取的,所以可以修复 RandomAccessFile randomAccessFile = new RandomAccessFile(path, "rw"); String result = null; while ((result=randomAccessFile.readLine())!=null){ 
    System.out.println("result "+result); //修复 System.out.println(new String(result.getBytes("ISO-8859-1"), "gb2312")); } 

可以看见,如果使用BufferedReader 读取字符串,那么在构造InputStreamReader最好指定正常的 编码格式。RandomAccessFile则要注意 他是用ISO-8859-1读取的。

小讯
上一篇 2025-02-18 18:00
下一篇 2025-03-08 08:44

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51921.html