packageexample.encoding;
importjava.io.BufferedWriter;
importjava.io.File;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.FileWriter;
importjava.io.IOException;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.io.UnsupportedEncodingException;
importjava.io.Writer;
importjava.nio.charset.Charset;
importjava.util.Iterator;
importjava.util.Set;
importjava.util.SortedMap;
/** *//**
*
* The Class IOEncodeTest is a tester class for java encoding. Mainnaly contains
* two parts:
* 1.Test written by FileWriter, with or without given character encoding value
* 2.Test written by OutputStreamWriter, with or without given character encoding value
*
*
*@authorPaul Lin
*@version1.0
*/
publicclassOutputEncodingTest
{
privatestaticString word="Hello world! 中国";
privatestaticfinalString ENCODING_EN="ISO-8859-1";
privatestaticfinalString ENCODING_CN="GB2312";
privatestaticfinalString ENCODING_UTF="UTF-8";
privatestaticfinalString DEFAULT_SYSTEM_ENCODING=System
.getProperty("file.encoding");
/** *//**
* The main method.
*
*@paramargs the arguments
*/
publicstaticvoidmain(String args[])
{
OutputEncodingTest tester=newOutputEncodingTest();
tester.testFileWriter();
tester.testOutputStreamWriter();
}
/** *//**
* Test file writer.
*/
publicvoidtestFileWriter()
{
//Create test result folder
String resultFolder=createResultFolder(System
.getProperty("user.language"), getBasePath());
//With default platform encoding
writeByFileWriter(word, resultFolder);
//With given system file.encoding property
writeByFileWriter(word, ENCODING_EN, resultFolder);
writeByFileWriter(word, ENCODING_CN, resultFolder);
writeByFileWriter(word, ENCODING_UTF, resultFolder);
}
/** *//**
* Test output stream writer.
*/
publicvoidtestOutputStreamWriter()
{
//Create test result folder
String resultFolder=createResultFolder(System
.getProperty("user.language"), getBasePath());
//With default platform encoding
writeByOutputStreamWriter(word, resultFolder);
//With given system file.encoding property
writeByOutputStreamWriter(word, ENCODING_EN, resultFolder);
writeByOutputStreamWriter(word, ENCODING_CN, resultFolder);
writeByOutputStreamWriter(word, ENCODING_UTF, resultFolder);
}
/** *//**
* Prints the available charset.
*/
publicvoidprintAvailableCharset()
{
SortedMapcharsets=Charset.availableCharsets();
SetcharsetKeys=charsets.keySet();
System.out.println("\n<<<+"Can encode >>>>\n");
Iteratori=charsetKeys.iterator();
while(i.hasNext())
{
String key=(String) i.next();
Charset charset=(Charset) charsets.get(key);
String displayName=charset.displayName();
booleancanEncode=charset.canEncode();
System.out.println(key+"-"+displayName+"-"+canEncode);
}
}
/** *//**
* Write by file writer.
*
*@paramcontent the content
*/
privatevoidwriteByFileWriter(String content, String destination)
{
String defaultEncoding=System.getProperty("file.encoding");
System.out.println("Using default system encoding:"+defaultEncoding);
writeByFileWriter(content, defaultEncoding, destination);
}
/** *//**
* Write by file writer.
*
*@paramcontent the content
*@paramencoding the encoding
*/
privatevoidwriteByFileWriter(String content, String encoding,
String destination)
{
printDebugInformation("FileWriter", encoding, content);
//Get system default encoding
String defaultEncoding=System.getProperty("file.encoding");
//Reset underlying platform character encoding
if(!defaultEncoding.equalsIgnoreCase(encoding))
{
System.setProperty("file.encoding", encoding);
}
//Save as file with given encoding value
String file=returnFileName(destination,"write_by_filewriter_",
encoding,".txt");
try
{
Writer writer=newBufferedWriter(newFileWriter(file));
writer.write(content);
writer.flush();
writer.close();
}catch(IOException ioe)
{
ioe.printStackTrace();
}
//Reset character encoding to system default value
resetDefaultSystemEncoding();
}
/** *//**
* Write by output stream writer.
*
*@paramcontent the content
*/
privatevoidwriteByOutputStreamWriter(String content, String destination)
{
String defaultEncoding=System.getProperty("file.encoding");
System.out.println("Using default system encoding:"+defaultEncoding);
writeByOutputStreamWriter(content, defaultEncoding, destination);
}
/** *//**
* Write by output stream writer.
*
*@paramcontent the content
*@paramencoding the encoding
*/
privatevoidwriteByOutputStreamWriter(String content, String encoding,
String destination)
{
printDebugInformation("OutputStreamWriter", encoding, content);
//Save as file with given encoding value
String file=returnFileName(destination,
"write_by_outputStreamWriter_", encoding,".txt");
try
{
Writer writer=newPrintWriter(
newBufferedWriter(newOutputStreamWriter(
newFileOutputStream(file), encoding)));
writer.write(content);
writer.flush();
writer.close();
}catch(FileNotFoundException fnfe)
{
fnfe.printStackTrace();
}catch(UnsupportedEncodingException uee)
{
uee.printStackTrace();
}catch(IOException ioe)
{
ioe.printStackTrace();
}
//Reset character encoding to system default value
resetDefaultSystemEncoding();
}
/** *//**
* Gets the base path.
*
*@returnthe base path
*/
privateString getBasePath()
{
StringBuffer finalPath=newStringBuffer();
String dir=System.getProperty("user.dir");
finalPath.append(dir);
finalPath.append((dir.endsWith("\\")||dir.endsWith("/"))?"":"/");
finalPath.append("src").append("/");
finalPath.append("example").append("/");
finalPath.append("encoding").append("/");
returnfinalPath.toString();
}
/** *//**
* Return file name.
*
*@parambasePath the base path
*@paramprefix the prefix
*@paramcontent the content
*@paramsubfix the subfix
*
*@returnthe string
*/
privateString returnFileName(String basePath, String prefix,
String content, String subfix)
{
StringBuffer name=newStringBuffer(basePath);
if((!basePath.endsWith("\\")&&(!basePath.endsWith("/"))))
{
name.append("/");
}
name.append(prefix);
name.append(content);
name.append(subfix);
returnname.toString();
}
/** *//**
* Creates the result folder.
*
*@paramplatform the platform
*@paramfullPath the full path
*
*@returnthe string
*/
privateString createResultFolder(String platform, String fullPath)
{
StringBuffer resultFolder=newStringBuffer();
if(fullPath.endsWith("\\")||fullPath.endsWith("/"))
{
resultFolder.append(fullPath);
}else
{
resultFolder.append(fullPath).append("/");
}
resultFolder.append("Test_Result_Of_").append(platform);
File file=newFile(resultFolder.toString());
if(!file.exists())
{
file=newFile(resultFolder.toString());
file.mkdir();
returnresultFolder.toString();
}else
{
returnfile.getAbsolutePath();
}
}
/** *//**
* Prints the debug information.
*
*@paramwriterName the writer name
*@paramencoding the encoding
*/
privatevoidprintDebugInformation(String writerName, String encoding,
String content)
{
StringBuffer msg=newStringBuffer();
msg.append("\n<<<
msg.append("Test written by").append(writerName);
msg.append("with encoding").append(encoding);
msg.append("---------------------------------->>>>\n");
msg.append("\nOriginal string:").append(content).append("\n");
System.out.println(msg.toString());
}
/** *//**
* Reset default system encoding.
*/
privatevoidresetDefaultSystemEncoding()
{
System.setProperty("file.encoding", DEFAULT_SYSTEM_ENCODING);
}
}
【1】中文平台情况下,测试结果如下:
1.如果采用FileWriter,并指定GBK编码:编码后字符长度为15,可以正常保存和读取
2.如果采用FileWriter,并指定UTF-8编码:编码后字节长度为16,可以正常保存和读取
3.如果采用FileWriter,并指定ISO8859-1编码:编码后字节长度为17,可以正常保存和读取
4.如果采用OutputStreamWriter,并指定GBK编码:编码后字符长度为15,可以正常保存和读取
5.如果采用OutputStreamWriter,并指定UTF-8编码:编码后字节长度为16,可以正常保存和读取
6.如果采用OutputStreamWriter,并指定ISO-8859-1编码:编码后字节长度为17,变成?
【2】英文平台情况下,测试结果如下:
1.如果采用FileWriter,并指定GBK编码:编码后字符长度为15,变成?
2.如果采用FileWriter,并指定UTF-8编码:编码后字节长度为16,变成?
3.如果采用FileWriter,并指定ISO-8859-1编码:编码后字节长度为17,变成?
4.如果采用OutputStreamWriter,并指定GBK编码:编码后字符长度为15,可以正常保存和读取
5.如果采用OutputStreamWriter,并指定UTF-8编码:编码后字节长度为16,可以正常保存和读取
6.如果采用OutputStreamWriter,并指定ISO-8859-1编码:编码后字节长度为17,变成?
【结论】
①在中文平台下,如果使用FileWriter,不论你如何设置字符集都不会起作用。因为它采用的是默认的系统字符集。即便你设置了System.setProperty("file.encoding", "ISO-8859-1"),或者在运行时给予参数-Dfile.encoding=UTF-8都不会起作用。你会发现它最终还是都已"GB2312"或者"GBK"的方式保存。
在中文平台下,如果使用OutputStreamWriter,则在后台写入时会把字符流转换成字节流,此时指定的编码字符集就起作用了。可以看到在指定GBK、UTF-8的情况下中文可以正常的保存和读取,同时文件按照我们给定的方式保存了。而对于ISO-8859-1则变成了?,这再次证明了采用ISO-8859-1是不能保存中文的,而且会因为中文编码在ISO-8859-1的编码中找不到对应的字符而默认转换成?。
②在英文平台下,如果使用FileWriter,不论你如何设置字符集同样都不会起作用。所有的文件都将按照ISO-8859-1的编码方式保存,毫无疑问地变成了?。在英文平台下,如果使用OutputStreamWriter,则只有当我们把字符和文件的编码方式正确设置为GBK、UTF-8的情况下,中文才能正确的保存并显示。
③通过上述的实验证明,为了确保在不同的平台下,客户端输入的中文可以被正确地解析、保存、读取。最好的办法就是使用OutputStreamWriter配合UTF-8编码。
如果不想使用UTF-8编码,那么可以考虑使用GB2312,不建议使用GBK、GB18030。因为对于某些老式的文本编辑器,甚至不支持GBK、GB18030的编码,但是对于GB2312则是一定支持的。因为前两者都不是国标但后者是。
④关于String的getBytes(),getBytes(encoding)和new String(bytes, encoding)这三个方法,非常值得注意:
A.getBytes():使用平台默认的编码方式(通过file.encoding属性获取)方式来将字符串转换成byte[]。得到的是字符串最原始的字节编码值。
B.getBytes(NAME_OF_CHARSET):使用指定的编码方式将字符串转换成byte[],如果想要得到正确的字节数组,程序员必须给出正确的NAME_OF_CHARSET。否则得到的就不会得到正确的结果。
C.new String(bytes, encoding):如果我们在客户端使用UTF-8编码的JSP页面发出请求,浏览器编码后的UTF-8字节会以ISO-8859-1的形式传递到服务器端。所以要得到经HTTP协议传输的原始字节,我们需要先调用getBytes("ISO-8859-1")得到原始的字节,但由于我们客户端的原始编码是UTF-8,如果继续按照ISO-8859-1解码,那么得到的将不是一个中文字符,而是3个乱码的字符。所以我们需要再次调用new String(bytes,"UTF-8"),将字节数组按照UTF-8的格式,每3个一组进行解码,才能还原为客户端的原始字符。
D.String的getBytes()、getBytes(NAME_OF_CHARSET)方法都是比较微妙的方法,原则上:传输时采用的是什么编码,我们就需要按照这种编码得到字节。new String(bytes, NAME_OF_CHARSET)则更加需要小心,原则上:客户端采用的是什么编码,那么这里的NAME_OF_CHARSET就必须和客户端保持一致。
例如JSP页面是GBK,那么我们接收页面传递而来的参数时就必须使用new String(parameter.getBytes("ISO-8859-1"), "GBK");如果使用了错误的解码方式,如使用了UTF-8,那么得到的很有可能就是乱码了。
也就是说:GBK--->ISO-8859-1--->GBK、UTF-8--->ISO-8859-1--->UTF-8的转换过程是没有问题的。但是GBK--->ISO-8859-1--->UTF-8、UTF-8--->ISO-8859-1--->GBK的字节直接转码则可能导致乱码,需要另外的转换过程。
记住:
谨慎地使用getBytes(NAME_OF_CHARSET)和new String(bytes, NAME_OF_CHARSET),除非你很清楚的知道原始的字符编码和传输协议使用的编码。
推荐使用基于服务器的配置、过滤器设置request/response的characterEncoding、content type属性。还有就是JSP页面的pageEncoding属性、HTML meta元素的content type属性。尽量避免频繁的在代码中进行字符串转码,即降低了效率又增加了风险