乱码产生的原因以及解决方案
所有数据都是以流的方式进行传输与存储的,比如读取一个文件的数据,将文件数据写入到输入流中,程序从输入流中读取数据,保存数据时,程序将数据写入到输出流中,并最终将输出流中的数据写入到文件。所以不管是输出还是输入,都是使用的流,但流中其实全是以字节表示。
所以,当我们在数据的传输或存储过程中,如果没有使用正确的编码进行转换,则会出现规则值与编码规则不对应的情况 ,即一些中文或符号在该编码规则下表现成为一些奇特的符号,我们通常称之为乱码。
-
产生乱码原因一:
-
产生乱码原因一:
比如'中'字,按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转换成字符‘中’,这是一个不可逆的操作。
输出结果:锟斤拷锟斤拷锟斤拷锟角非碉拷失
解决方案:
使用正确的编码进行转换,如果在不明确编码的情况下,可先将字节流转成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();
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)
{
if (inBuff != null)
{
try
{
inBuff.close();
}catch(Exception e){}
{
inBuff.close();
}catch(Exception e){}
}
if (outBuff != null)
{
{
try
{
outBuff.close();
}catch(Exception e){}
{
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就能得到原始字节流。
-
网页提交字符串
-
网页提交字符串
在服务器端,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") 重新得到正确的字符串 "中"。
-
从数据库读取字符串
-
从数据库读取字符串
比如Mysql4.*,默认的字符编码为latin1(ISO-8859-1),如果从数据库读取字符串时得到乱码,而数据库中存放的数据又是正确的,那么往往还是因为前面提到的原因一造成的,解决的办法还是通过 string = new String(string.getBytes("iso-8859-1"), "GB2312") 的方法,重新得到原始的字节串,再重新使用正确的编码转化成字符串。
保存到数据库的时候,也需要转换后在保存。 string = new String(string.getBytes("GB2312"), "iso-8859-1")
- 非 UNICODE 程序在不同语言环境间移植时的乱码