文档章节

Java GBK中文乱码问题分析

s
 skyshitt
发布于 2016/02/11 03:51
字数 1037
阅读 3539
收藏 127
点赞 14
评论 14

在io相关的操作中经常会出现乱码问题


比如在一个txt文件中按GBK编码保存内容"淘!我喜欢!"

然后用RandomAccessFile类读取并打印一行。

RandomAccessFile raf = new RandomAccessFile("D:\\1.txt","r");
System.out.print(raf.readLine());

打印结果显示乱码:


在网上查询到加入相关编码解码操作后可以解决该问题

RandomAccessFile raf = new RandomAccessFile("D:\\1.txt","r");
System.out.print(new String(raf.readLine().getBytes("ISO-8859-1"),"gbk"));


问题:

在这个过程中发生了什么?


要解答这个问题首先要知道编码和解码的概念以及产生的原因:

为什么要编码

不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次 翻译,把它翻译成英语。这个翻译的过程就是编码。所以可以想象只要不是说英语的国家要能够使用计算机就必须要经过编码。这看起来有些霸道,但是这就是现 状,这也和我们国家现在在大力推广汉语一样,希望其它国家都会说汉语,以后其它的语言都翻译成汉语,我们可以把计算机中存储信息的最小单位改成汉字,这样 我们就不存在编码问题了。

所以总的来说,编码的原因可以总结为:

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

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

    3.要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码。

名词解释:

解码:将byte数组转为char数组。

编码:将char数组转为byte数组。


计算机存储的基本单位是byte,但打开一个文件时文件编辑器已经做了解码的工作。

以下为解码过程描述

文件实际存储的内容是(以下为16进制):

打开文件后看到的内容为

需要详细说明以下代码的处理过程

RandomAccessFile raf= new RandomAccessFile("D:\\1.txt","r");
System.out.print(raf.readLine());

首先看一下java.io.RandomAccessFile#readLine方法的源码

public final String readLine() throws IOException {
        StringBuffer input = new StringBuffer();
        int c = -1;
        boolean eol = false;
        while (!eol) {
            switch (c = read()) {
            case -1:
            case '\n':
                eol = true;
                break;
            case '\r':
                eol = true;
                long cur = getFilePointer();
                if ((read()) != '\n') {
                    seek(cur);
                }
                break;
            default:
                input.append((char)c);
                break;
            }
        }
        if ((c == -1) && (input.length() == 0)) {
            return null;
        }
        return input.toString();
    }

主要关注read()部分和(char)c,read()是一个本地方法,作用是从文件中读取一个byte字节。

(char)c是将变量c从byte类型转换为char类型,这是一个解码操作。

问题:此处是以什么格式进行解码?

解码格式是ISO-8859-1

raf.readLine()的处理过程如下


那么

new String(raf.readLine().getBytes("ISO-8859-1"),"gbk")

这行代码做了什么

首先readLine()按行一字节一字节地读取文件中的数据,并且按ISO-8859-1进行解码拼成char数组,然后getBytes("ISO-8859-1")对拼成后的char数组按ISO-8859-1进行编码获取byte数组,最后new String(string,"gbk")对编码后的byte数组用gbk格式进行解码成char数组。


参考资料:深入分析 Java 中的中文编码问题


遗留问题:

1、如何避免重复转码。

2、RandomAccessFile的readLine()效率非常低,如何提高效率。

© 著作权归作者所有

共有 人打赏支持
s
粉丝 1
博文 1
码字总数 1037
作品 0
程序员
加载中

评论(14)

cys1357
cys1357
File file = new File("D:\\1.txt");   
FileInputStream fis = new FileInputStream(file);
  FileChannel channel=fis.getChannel();
  int size=(int) channel.size();
  ByteBuffer buffer=ByteBuffer.allocate(size);
  channel.read(buffer);
  String b=new String(buffer.array(),"gbk");
  System.out.print(b);
或者
File file = new File("D:\\1.txt");
byte[] tempchars =new byte30;
  BufferedInputStream reader = null;
  reader = new BufferedInputStream(new FileInputStream(file));
reader.read(tempchars);
  String b=new String(tempchars,"gbk");
  System.out.print(b);
都可以打印出正常字符
淘!我喜欢!
其实问题出在楼主读文件时使用的方法假定了文件内容是字符,
java对字符的处理显然有一些默认的假设,这些假设中显然没有考虑gb系列编码,所以将其当作别的字符集字符,于是做了错误的转换。其实只要对文件读处理时不做任何字符假设,将文件内容作为纯粹的字节数组读入,然后new String时指明这个数组中存放的内容是gbk编码的字符,java就可以正确打印出字符串。
李文轩
李文轩
Reader:想成行读,就要存到String;存到String,就避免不了二次解码。
InputStream:想一次解码,就要自己判断行尾。
s
skyshitt

引用来自“赵开锦”的评论

推荐直接使用org.apache.commons.io.FileUtils把,一般不直接使用原生的文件读写方式。
FileUtils的确比RandomAccessFile类好用很多,而且效率更高。
s
skyshitt

引用来自“一堆BUG”的评论

2、RandomAccessFile的readLine()效率非常低,如何提高效率。
可以参考一下IBM文档
花1K内存实现高效I/O的RandomAccessFile类
https://www.ibm.com/developerworks/cn/java/l-javaio/
感谢提供参考资料。
s
skyshitt

引用来自“jianglibo”的评论

read()根本无需涉及编码问题。你用StringBuffer去保存,已经误用了。用byte[]才是正道。说起这个,我的编码检测代码对这个编码问题有比较深入的表达。http://git.oschina.net/jianglibo/char-encode-detector
StringBuffer部分的代码是jdk里边RandomAccessFile类的源码。所以平常如果不注意乱用的话就会出现乱码问题。
赵开锦
赵开锦
推荐直接使用org.apache.commons.io.FileUtils把,一般不直接使用原生的文件读写方式。
jianglibo
jianglibo

引用来自“sunnytu”的评论

最后一个图应该是不对的,应该是按照iso编码方式解码为byte[],然后在编码为char[]
read()时不存在编码问题,就是读取一个byte。“然后编码为char[]",这句话是错的,从byte到char叫解码,从char到byte叫编码。
sunnytu
sunnytu
最后一个图应该是不对的,应该是按照iso编码方式解码为byte[],然后在编码为char[]
阿信sxq
阿信sxq

引用来自“jianglibo”的评论

想查看一个文件的内容,必须知道文件的编码格式。一个文件就是一个byte[],两者的内容是等价的,byte[]可能是gbk的字串,可能是mp3,也可能是jpg.

引用来自“Feng_Yu”的评论

GBK,GB2312这两编码有何不同,以及GB18030?一个是另一个的子集么?实际使用的时候我发现这两个编码几乎通用。 在windows的python编码甚至还有cp936来指代GBK的。实在对这些繁杂的编码有些头大。期待UTF-8大一统的美好世界
不管怎么样,不同的操作系统就是一个坑,不同操作系统的编码不一样,特别是windows,最大的坑
jianglibo
jianglibo

引用来自“jianglibo”的评论

想查看一个文件的内容,必须知道文件的编码格式。一个文件就是一个byte[],两者的内容是等价的,byte[]可能是gbk的字串,可能是mp3,也可能是jpg.

引用来自“Feng_Yu”的评论

GBK,GB2312这两编码有何不同,以及GB18030?一个是另一个的子集么?实际使用的时候我发现这两个编码几乎通用。 在windows的python编码甚至还有cp936来指代GBK的。实在对这些繁杂的编码有些头大。期待UTF-8大一统的美好世界
可以在wikipedia找到详细的解释。如果有时间的话,可以帮我的项目实现一个检测日语编码的实现。只要继承一个abstract类即可。它会一个byte,2个byte,3个byte地喂给你的类,只要按照规则匹配即可。这样对于编码问题就会非常熟悉了。
Tomcat中文乱码问题的原理和解决方法

Tomcat中文乱码问题的原理和解决方法   自从接触Java和JSP以来,就不断与Java的中文乱码问题打交道,现在终于得到了彻底的解决,现将我们的解决心得与大家共享。   一、Java中文问题的由...

孙斐
2013/02/26
0
0
MenuItem 显示中文乱码问题解决方案

MenuItem 显示中文乱码问题解决方案 今天在使用Java做系统托盘图标(TrayIcon),需要为其增加一个右键弹出菜单(PopupMenu),在使用菜单项(MenuItem)时,遇到了一个非常痛苦的事情:中文乱码~...

Java编程思想
2013/10/17
0
0
JavaWeb的各种中文乱码分析与解决

一、Servlet输出乱码 1. 用servlet.getOutStream字节流输出中文,假设要输出的是String str ="ooxx是中国的,无耻才是日本的"。 1.1 若是本地服务器与本地客户端这种就不用说了,直接可以out...

大数据之路
2012/10/05
0
0
mysql保存中文乱码的原因和解决办法

mysql保存中文乱码的原因和解决办法2012-09-19 10:52:49 我来说两句 作者:androidjiaocheng收藏 我要投稿 当你遇到这个mysql保存中文乱码问题的时候,期待找到mysql保存中文乱码的原因和解决...

fcsong000833
2013/01/08
0
0
使用ubuntu 10.04中的中文乱码问题解决

1、ibus输入法 Ubuntu 系统安装后已经自带了ibus输入法,在英语环境下默认不启动。 配置ibus自动启动可以在ubuntu系统菜单上选择System --- Preferences --- Startup Applications,在该窗口...

durban
2012/08/25
0
0
JAVA之编码/解码 -- 各种环境下可能会发生的乱码问题及解决方案

工作中经常遇到java编码问题,由于缺乏研究,总是无法给出确切的答案,这个周末在网上查了一些资料,在此做些汇总。 问题一:在java中读取文件时应该采用什么编码? Java读取文件的方式总体可...

roockee
2013/10/22
0
0
eclipse下properties文件中文乱码的解决方案

在中文操作系统下,Eclipse中的Java类型文件的编码的默认设置是GBK,但是对Properties资源文件的编码的默认设置是ISO-8859-1。所以编辑Java文件中的中文不会出现问题,但编辑Properties资源文...

xinlll
2012/11/01
0
0
Activiti5.9换成MySQL数据库

本人博客开始迁移,博客整个架构自己搭建及编码 http://www.cookqq.com/listBlog.action Activiti5.9默认的数据库是db,可是我们项目中运用的是mysql,那只好把db换成mysql 步骤: 1.下载act...

cookqq
2013/03/08
0
0
关于java发送http请求时中文乱码的一种解决办法

在jsp中常见的乱码解决办法无外乎是关于get和post两种方式的,但只有切实地在实践中使用时才会注意或者说注重到其他方式。例如,在http请求头中传送中文参数,出现乱码,如何解决? 实际场景...

chace0120
2014/03/13
0
0
Java编码和servlet乱码问题(1)-Java编码

在写这边文章时,在网上扒拉了很久,发现好多博客总结的都非常好,我也是参考了很多,在文章最后会列出一些觉得不错的博客地址,大家如果有兴趣可以去看看。 本篇主要是简单介绍Java编码知识...

阿山du
2013/12/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

expect(spawn) 自动化git提交和scp拷贝---centos(linux)

**在进行SCP文件拷贝中,往往需要进行用户密码的输入,即用户交互。若采用自动化脚本的方式进行,则可用以下方式: ** #!/usr/bin/expect #设置参数 set src [lindex $argv 0] set dest [lin...

helplove
6分钟前
1
0
用Build来构建对象的写法

如果一个类的属性过多,用构造器来构建对象很难写,因此我们时用Build方式来构建对象。写法大致如下。 import java.io.Serializable;import java.util.Date;public class Log impleme...

算法之名
9分钟前
11
0
利用 acme.sh 获取网站证书并配置https访问

acme.sh 实现了 acme 协议, 可以从 letsencrypt 生成免费的证书.(https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) 主要步骤: 安装 acme.sh 生成证书 copy 证书到 nginx/ap...

haoyuehong
22分钟前
2
0
微擎框架内如何根据media_id获取到微信图片的路径

微擎的框架内,图片选择后,获取的是那个字符串是media_id,相当于你这张图片在微信的图片服务器里面的id 要求是:获取https://mmbiz.qpic.cn/mmbiz_jpg/…… 微信图片的路径 而微信并没有根据m...

老bia同学
26分钟前
1
0
Spring boot中日期的json格式化

Model 在model层中,类的日期属性上面添加如下注解: @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") 参考 Jackson Date格式化教程...

亚林瓜子
27分钟前
2
0
Eclipse:Failed to load the JNI shared library

1.问题背景: 由于我之前使用jdk1.9学习,当使用Luke的时候发现jdk版本过高,需要向下配置jdk,就向朋友拷了一个安装包。重新配置路径后,便开始报错。 2.问题描述: Failed to load the JNI...

tinder_boy
30分钟前
1
0
少儿学习编程课程是否真的适合七八岁的低龄儿童[图]

少儿学习编程课程是否真的适合七八岁的低龄儿童[图]: 天下熙熙皆为利来,天下攘攘皆为利往。 这几年来,乐高教育机构在国内如同雨后春笋般出现,当然关闭/转手的也很多。从教师角度来看,部...

原创小博客
35分钟前
1
0
ES12-词项查询

1.词项查询介绍 全文查询将在执行之前分析查询字符串,但词项级别查询将按照存储在倒排索引中的词项进行精确操作。这些查询通常用于数字,日期和枚举等结构化数据,而不是全文本字段。 或者,...

贾峰uk
43分钟前
2
0
http状态码与ajax的状态值

ajax状态值 1.1 200 & OK:状态请求成功

litCabbage
46分钟前
2
0
iOS动画效果合集、飞吧企鹅游戏、换肤方案、画板、文字效果等源码

iOS精选源码 动画知识运用及常见动画效果收集 3D卡片拖拽卡片叠加卡片 iFIERO - FLYING PENGUIN 飞吧企鹅SpriteKit游戏(源码) Swift封装的空数据提醒界面EmptyView 沙盒文件浏览与分享调试控...

sunnyaigd
49分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部