`

【Java基础专题】编码与乱码(04)---输出时的编码与乱码

 
阅读更多
package example.encoding;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;

/** *//**
 * <pre>
 * 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
 * </pre>
 * 
 * @author Paul Lin
 * @version 1.0
 */
public class OutputEncodingTest {

    private static String word = "Hello world! 中国";

    private static final String ENCODING_EN = "ISO-8859-1";

    private static final String ENCODING_CN = "GB2312";

    private static final String ENCODING_UTF = "UTF-8";

    private static final String DEFAULT_SYSTEM_ENCODING = System
            .getProperty("file.encoding");

    /** *//**
     * The main method.
     * 
     * @param args the arguments
     */
    public static void main(String args[]) {
        OutputEncodingTest tester = new OutputEncodingTest();
        tester.testFileWriter();
        tester.testOutputStreamWriter();
    }

    /** *//**
     * Test file writer.
     */
    public void testFileWriter() {
        // 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.
     */
    public void testOutputStreamWriter() {
        // 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.
     */
    public void printAvailableCharset() {
        SortedMap<String, Charset> charsets = Charset.availableCharsets();
        Set<String> charsetKeys = charsets.keySet();
        System.out.println("\n<<<< Canonical name -- Display name -- "
                + " Can encode >>>>\n");

        Iterator<String> i = charsetKeys.iterator();
        while (i.hasNext()) {
            String key = (String) i.next();
            Charset charset = (Charset) charsets.get(key);
            String displayName = charset.displayName();
            boolean canEncode = charset.canEncode();
            System.out.println(key + " - " + displayName + " - " + canEncode);
        }
    }

    /** *//**
     * Write by file writer.
     * 
     * @param content the content
     */
    private void writeByFileWriter(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.
     * 
     * @param content the content
     * @param encoding the encoding
     */
    private void writeByFileWriter(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 = new BufferedWriter(new FileWriter(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.
     * 
     * @param content the content
     */
    private void writeByOutputStreamWriter(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.
     * 
     * @param content the content
     * @param encoding the encoding
     */
    private void writeByOutputStreamWriter(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 = new PrintWriter(
                    new BufferedWriter(new OutputStreamWriter(
                            new FileOutputStream(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.
     * 
     * @return the base path
     */
    private String getBasePath() {
        StringBuffer finalPath = new StringBuffer();
        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("/");
        return finalPath.toString();
    }

    /** *//**
     * Return file name.
     * 
     * @param basePath the base path
     * @param prefix the prefix
     * @param content the content
     * @param subfix the subfix
     * 
     * @return the string
     */
    private String returnFileName(String basePath, String prefix,
            String content, String subfix) {
        StringBuffer name = new StringBuffer(basePath);
        if ((!basePath.endsWith("\\") && (!basePath.endsWith("/")))) {
            name.append("/");
        }
        name.append(prefix);
        name.append(content);
        name.append(subfix);
        return name.toString();
    }

    /** *//**
     * Creates the result folder.
     * 
     * @param platform the platform
     * @param fullPath the full path
     * 
     * @return the string
     */
    private String createResultFolder(String platform, String fullPath) {
        StringBuffer resultFolder = new StringBuffer();

        if (fullPath.endsWith("\\") || fullPath.endsWith("/")) {
            resultFolder.append(fullPath);
        } else {
            resultFolder.append(fullPath).append("/");
        }
        resultFolder.append("Test_Result_Of_").append(platform);

        File file = new File(resultFolder.toString());
        if (!file.exists()) {
            file = new File(resultFolder.toString());
            file.mkdir();
            return resultFolder.toString();
        } else {
            return file.getAbsolutePath();
        }
    }

    /** *//**
     * Prints the debug information.
     * 
     * @param writerName the writer name
     * @param encoding the encoding
     */
    private void printDebugInformation(String writerName, String encoding,
            String content) {
        StringBuffer msg = new StringBuffer();
        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.
     */
    private void resetDefaultSystemEncoding() {
        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属性。尽量避免频繁的在代码中进行字符串转码,即降低了效率又增加了风险

分享到:
评论

相关推荐

    JAVA字符编码:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换

    ### JAVA字符编码详解:Unicode, ISO-8859-1, GBK, ...同时,我们还注意到,在使用不当的编码进行转换时可能会导致乱码,但通过适当的交叉转换方法,仍有可能恢复原始信息。这对于开发者来说是一个非常有价值的技巧。

    java中文乱码问题详解--- java中文乱码问题详解

    #### 三、Java编码转换的详细过程 Java程序从开发到运行的过程中,涉及到多个环节的编码转换,主要包括: 1. **源文件编码**:Java源文件(`.java`)在保存时通常采用操作系统默认的编码格式。例如,在中文Windows...

    java 编码 UTF-8、ISO-8859-1、GBK

    Java 编码问题一直是开发者们面临的一大挑战,尤其是在处理中文字符时。UTF-8、ISO-8859-1 和 GBK 是三种常见的字符编码格式,每种都有其特定的应用场景和优缺点。 首先,UTF-8 是一种广泛使用的多字节编码,能够...

    Java避免UTF-8的csv文件打开中文出现乱码的方法

    Java避免UTF-8的csv文件打开中文出现...Java避免UTF-8的csv文件打开中文出现乱码的方法是使用UTF-16LE编码格式,并在文件头部输出BOM。同时,需要考虑Excel版本的兼容性问题,以确保csv文件可以正确地被打开和读取。

    有关pdfbox-1.3.1中Identity-H编码为乱码的解决方法

    解决Identity-H编码乱码问题通常涉及以下几个步骤: 1. **检查字体资源**:确保PDFBox能够访问并正确解析字体文件。PDF文档中可能引用了特定的字体,比如Arial或Times New Roman,而系统中必须有这些字体的TrueType...

    乱码 编码方式解决 gbk ISO8859-1 utf8 编码

    1. **编码不匹配**:当读取或显示文件时使用的编码与文件实际编码不一致时,就会出现乱码。 2. **系统默认编码设置错误**:如果系统默认的编码设置与文件的实际编码不一致,也可能导致乱码问题。 3. **软件编码识别...

    解决中文乱码问题-java

    当从不同的源(如网络请求、文件读取等)获取字符串时,必须明确知道其原始编码格式,并在必要时进行转换,以便与当前系统或程序所使用的编码格式相匹配。在Java中,常用的编码有`ISO-8859-1`(也称为`latin1`)、`...

    Java中压缩与解压--中文文件名乱码解决办法

    尤其是在Windows系统下,文件名通常采用GB2312或GBK编码,而Java中的`ZipInputStream`和`ZipOutputStream`类默认使用的是Unicode编码,这种编码差异导致了中文文件名在压缩与解压过程中的乱码现象。 #### 二、解决...

    java读写csv文件,中文乱码问题

    - 解决Java处理CSV文件时的中文乱码问题,关键在于正确指定字符编码。 - 要注意文件的读取、写入以及处理过程中的编码一致性。 - 使用专门的CSV处理库能简化操作,并提供更好的兼容性。 - 对于复杂情况,了解并...

    jd-gui java反编译中文复制无乱码jd-gui

    jd-gui.exe反编译后复制中文是乱码,xml,yml...等配置文件中文显示为乱码,本资源修改了编码,反编译后中文随意复制,配置文件显示也是正常的

    关于JAVA字符编码:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换

    在Java中,如果使用ISO-8859-1编码获取Unicode字符时,可能会出现乱码,因为一个Unicode字符可能无法准确地转换成一个ISO-8859-1字节。 #### 3. GBK GBK是中国大陆地区广泛使用的一种汉字编码标准,它是对GB2312...

    Java中文乱码浅析及解决方案

    其次,当涉及文件读写或网络数据传输时,输出流和输入流的编码必须与预期编码一致。例如,当我们写入文件时,应明确指定输出流的编码为UTF-8: ```java OutputStream outputStream = new FileOutputStream("output....

    eclipse中class乱码GBK-UTF-8转换工具

    然而,当涉及到字符编码时,如GBK和UTF-8之间的转换,可能会遇到乱码问题。这篇博客文章“eclipse中class乱码GBK-UTF-8转换工具”正是为了解决这个问题。 GBK是GB2312的扩展,包含了更多的汉字和其他字符,主要在...

    数据库编程之编码转换实例--乱码分析

    当原始数据的编码与系统识别的编码不匹配时,就会出现无法正确显示的字符,也就是我们常说的乱码。 在数据库编程中,编码问题可能出现在以下几个环节: 1. 数据库设置:数据库管理系统(如MySQL、Oracle、SQL ...

    java中文乱码之解决URL中文乱码问题的方法

    在Java开发中,遇到中文乱码问题是一种常见的挑战,特别是在处理URL时。URL中文乱码问题主要是由于URL编码和解码过程中的不一致导致的。下面将详细介绍如何解决这个问题,并探讨几种常用的方法。 首先,我们需要...

    JAVA文件编码格式转换:UTF-8转为GB2312

    能够集成到Source Insight中,解决JAVA文件乱码问题

    Linux中java log输出中文乱码.docx

    在解决 Java log 输出中文乱码问题时,还需要关注 Java 应用程序的编码方式。如果 Java 应用程序使用的编码方式与 locale 设置不符,也将导致中文乱码问题。 在 Java 中,可以使用以下方式来设置编码方式: System...

    Java关于中文乱码问题的多种解决方法

    连接数据库时,确保数据库表的字符集与Java程序使用的编码一致。在JDBC连接字符串中,可以通过`useUnicode=true&characterEncoding=UTF-8`来指定字符集。此外,执行SQL语句时,如果涉及到字符串常量,也需要使用`...

Global site tag (gtag.js) - Google Analytics