Java操作:中文乱码解决方案

来源:互联网 发布:搜图用什么软件 编辑:程序博客网 时间:2024/05/18 10:48

阅读该文之前,需要对字节、字符与编码有所了解,否则将不容易理解。可参照《字节、字符与编码
乱码产生的原因以及解决方案

所有数据都是以流的方式进行传输与存储的,比如读取一个文件的数据,将文件数据写入到输入流中,程序从输入流中读取数据,保存数据时,程序将数据写入到输出流中,并最终将输出流中的数据写入到文件。所以不管是输出还是输入,都是使用的流,但流中其实全是以字节表示。
所以,当我们在数据的传输或存储过程中,如果没有使用正确的编码进行转换,则会出现规则值与编码规则不对应的情况 ,即一些中文或符号在该编码规则下表现成为一些奇特的符号,我们通常称之为乱码。


    • 产生乱码原因一:
在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。
比如'中'字,按GB2312生成[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 "ÖÐ",即为我们通常所说的乱码。
代码:

byte[] bys = "中".getBytes("GB2312");
String result = new String(bys, "ISO-8859-1");
System.out.println(result);

输出结果:ÖÐ
解决方案:

使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作,得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, "GB2312"),来得到正确的“UNICODE 字符串”。


    • 产生乱码原因二:
使用错误的编码进行转换,导致字符丢失或没有正确的映射关系,从而产生乱码。
比如:byte[] bys = "中".getBytes("ISO-8859-1");由于字符‘中’并不在编码‘ISO-8859-1’的映射规则内,故映射了一个符号‘?’,而‘?’在UNICODE中的映射值为63,故得到bys长度为1,且值为63,不论怎么转换,我们都不能将63转换成字符‘中’,这是一个不可逆的操作。
输出结果:锟斤拷锟斤拷锟斤拷锟角非碉拷失
代码:

String te = "顶起顶起是非得失 ";
String ret = new String(te.getBytes("GBK"), "UTF-8");
ret = new String(ret.getBytes("UTF-8"), "GBK");
System.out.println(ret);

解决方案:

使用正确的编码进行转换,如果在不明确编码的情况下,可先将字节流转成ISO-8859-1编码的字节流进行发送与接收,并在相应处理方进行对应编码转换。
代码:

String te = "顶起顶起是非得失 ";
String ret = new String(te.getBytes("GBK"), "ISO-8859-1");
ret = new String(ret.getBytes("ISO-8859-1"), "GBK");
System.out.println(ret);
输出结果:顶起顶起是非得失因为ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF。在其编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。
  • 产生乱码原因三:
当前平台不支持应用使用的字符编码,没有正确的映射关系,从而产生乱码。
经典案例

  • 文件上传或下载
在文件上传或下载过程中,由于文件编码的不确定性,不能强制进行编码转换,所以使用字节流的方式进行文件读取与保存,避免在中间环节进行编码转换,从而避免乱码的产生。

BufferedInputStream inBuff = null;
BufferedOutputStream outBuff = null;
try
{
// 新建文件输入流并对它进行缓冲
inBuff = new BufferedInputStream(new FileInputStream(sourceFile));
// 新建文件输出流并对它进行缓冲
outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));
// 缓冲数组
byte[] b = new byte[5120];
int len;
while ((len = inBuff.read(b)) != -1)
{
outBuff.write(b, 0, len);
}
// 刷新此缓冲的输出流
outBuff.flush();

}
finally
{
// 关闭流
if (inBuff != null)
{

try
{
inBuff.close();
}catch(Exception e){}

}

if (outBuff != null)
{

try
{
outBuff.close();
}catch(Exception e){}

}

}


    • 文件拷贝或移动
同‘文件上传或下载’


    • 读取文件内容并发到其它服务器
如果是需要读取文件全部内容并发送,可参照‘文件上传或下载’,使用字节流方式读取文件并进行发送
如果是读取部分内容并发送,则有如下二种情况
a)情况一:明确文件编码集,则直接使用文件编码进行文件的读写操作;
encoding="文件编码";
b)情况二:不明确文件编码集,不能盲目的使用字符编码进行转换,否则出现不可逆操作将导致乱码产生,但由于是读取文件内容,又必须得将字节转换成字符,如不指定编码,将会使用系统的默认编码,同样可能导致乱码产生,所以使用ISO-8859-1编码(目前该编码是唯一的可逆的)将字节流转换成字符流;
encoding="ISO-8859-1";
在Java中流的IO操作有二种:字节流与字符流
字节流基类:InputStream、OutputStream
字符流基类:Reader、Writer
字节流与字符流的综合:RandomAccessFile
方式一:在读取的时候就指定编码集(ISO-8859-1)
BufferedReader br = null;
StringBuffer info = new StringBuffer();
String lineSepara = System.getProperty("line.separator");
try
{
//取得命令结果的输出流
//用一个读输出流类去读
//用缓冲器读行
br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "encoding"));
String line=null;
//直到读完为止
while((line=br.readLine())!=null)
{
info.append(line).append(lineSepara);
}
}
finally
{
if(br != null)
{
try
{
br.close();
}
catch(Exception e){}
}
}
//在接收端转成指定编码
System.out.println(new String(info.toString().getBytes("ISO-8859-1"), "GBK"));
方式二:随机按行读文件,读取出来后按真实编码进行转换
RandomAccessFile access = null;
StringBuffer info = new StringBuffer();
String lineSepara = System.getProperty("line.separator");
try
{
//取得命令结果的输出流
//用一个读输出流类去读
//用缓冲器读行
access = new RandomAccessFile(filePath, "r");
String line=null;
//直到读完为止
while((line=access.readLine())!=null)
{
info.append(new String(line.getBytes("ISO-8859-1"), "encoding")).append(lineSepara);
}
}
finally
{
if(access != null)
{
try
{
access.close();
}
catch(Exception e){}
}
}
System.out.println(info.toString());
line.getBytes("ISO-8859-1")的原因:
RandomAccessFile.read()方法实际读取的内容是一个byte但返回的是一个int,所以在byte转int的时候,进行了位与以及补码操作,其实际原理与ISO-8859-1的处理原理一致,且映射关系相同,故使用ISO-8859-1就能得到原始字节流。


    • 网页提交字符串
当页面中的表单提交字符串时,首先把字符串按照当前页面的编码,转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如,一个编码为 GB2312 的页面,提交 "中" 这个字符串时,提交给服务器的内容为 "%D6%D0"。
在服务器端,Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节,然后再根据 GB2312 编码规则得到 "中" 字。
在 Tomcat 服务器中,request.getParameter() 得到乱码时,常常是因为前面提到的“原因一”造成的。默认情况下,当提交 "%D6%D0" 给 Tomcat 服务器时,request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符,而不是返回一个 "中" 字符。因此,我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串,再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。


    • 从数据库读取字符串
通过数据库客户端(比如 ODBC 或 JDBC)从数据库服务器中读取字符串时,客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时,客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。
比如Mysql4.*,默认的字符编码为latin1(ISO-8859-1),如果从数据库读取字符串时得到乱码,而数据库中存放的数据又是正确的,那么往往还是因为前面提到的原因一造成的,解决的办法还是通过 string = new String(string.getBytes("iso-8859-1"), "GB2312") 的方法,重新得到原始的字节串,再重新使用正确的编码转化成字符串。
保存到数据库的时候,也需要转换后在保存。 string = new String(string.getBytes("GB2312"), "iso-8859-1")
  • 非 UNICODE 程序在不同语言环境间移植时的乱码
0 0
原创粉丝点击