PrintWriter返回乱码的分析及解决

本文深入探讨了在Servlet环境下使用PrintWriter与ServletOutputStream输出中文字符时,如何正确设置字符编码以避免乱码问题。通过分析API文档和源代码实现,解释了设置字符编码的时机及机制,并对比了两种输出方式在字符编码设置上的差异。最终总结了在不同场景下选择合适的方法来确保中文输出的正确显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用response得到输出流,即response.getOuptStream(); 返回值为ServletOutputStream 对象,即JSP的out对象,要么用response得到输出对象PrintWriter即response.getWriter()。

  1. protected void doGet(HttpServletRequest request,      
  2.     HttpServletResponse response) throws ServletException,   
  3.         IOException {  
  4.  
  5.     PrintWriter pw = response.getWriter();        
  6.     response.setCharacterEncoding("utf-8");        
  7.     response.setContentType("text/html; charset=utf-8");        
  8.     pw.print("中文"); 
protected void doGet(HttpServletRequest request,     
    HttpServletResponse response) throws ServletException,  
        IOException { 

    PrintWriter pw = response.getWriter();       
    response.setCharacterEncoding("utf-8");       
    response.setContentType("text/html; charset=utf-8");       
    pw.print("中文");
}

输出乱码。为什么呢,已经设置了字符编码啊?难道设置的无效。

在API中找到方法说明:


  1. PrintWriter getWriter() throws IOException  
  2.  
  3. Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding().  
  4.  
  5. If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.  
PrintWriter getWriter() throws IOException 

Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). 

If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1. 

就是讲,在返回一个PrintWriter对象的时候,charactor encoding就已经确定了,就已经设置好了字符集了。什么时候设置的呢? setCharacterEncoding方法的实现时发现如下代码:


  1. public void setCharacterEncoding(String charset) {     
  2.     
  3.         if (isCommitted())     
  4.             return
  5.              
  6.         // Ignore any call from an included servlet     
  7.         if (included)     
  8.             return
  9.              
  10.         // Ignore any call made after the getWriter has been invoked     
  11.         // The default should be used     
  12.         if (usingWriter)     
  13.             return
  14.     
  15.         coyoteResponse.setCharacterEncoding(charset);     
  16.         isCharacterEncodingSet = true
  17.     }   
public void setCharacterEncoding(String charset) {    
   
        if (isCommitted())    
            return;
            
        // Ignore any call from an included servlet    
        if (included)    
            return;
            
        // Ignore any call made after the getWriter has been invoked    
        // The default should be used    
        if (usingWriter)    
            return;
   
        coyoteResponse.setCharacterEncoding(charset);    
        isCharacterEncodingSet = true;
    }  
其中usingWriter 标志为getPrinteWriter方法中设定,可见其控制逻辑为一旦返回了PrintWriter,本函数即不再生效。


ServletOutputStream out = response.getOutputStream();

out.print("中文");

情况1:正常,浏览器按utf-8方式查看
//response.setContentType("text/html; charset=utf-8");
           
情况2:浏览器缺省按简体中文查看,手动设为utf-8方式查看正常
//response.setCharacterEncoding("utf-8");
说明:这种方式不仅不需要在调用getOutputStream()之前设定字符集,甚至在print输出后设定都有效。

结论:

1.在servlet中输出中文,如果采用PrintWriter方式,需要在调用getPrintWriter()之前调用setContentType 或者 setCharacterEncoding;采用ServletOutputStream方式,不受此限。

2.setContentType 和 setCharacterEncoding两方法中设定characterEncoding的方法对服务器效果一致,不需要反复调用。在输出文本内容时, 采用response.setContentType("text/html; charset=utf-8");似乎更为方便。

3.PrintWriter自身并没有处理编码的职责,它还是应该看成一个装饰器比较好:它就是为了输出更方便而设计的,提供print、println、printf等便利方法。要设置编码的话,可以在它的底层Writer上设置:(这里以OutputStreamWriter为底层Writer),参考:


  1. new PrintWriter(new OutputStreamWriter(new FileOutputStream("yourfilepath"), "UTF-8"));  
  1. new PrintWriter(new OutputStreamWriter(new FileOutputStream("yourfilepath"), "UTF-8"));  
Java中,`ByteArrayOutputStream` 和 `PrintWriter` 是两个常用的类,分别用于处理字节流和字符流。如果你发现使用 `ByteArrayOutputStream` 结合 `PrintWriter` 时返回的内容为空,通常是由于以下原因之一: 1. **未正确刷新流**:`PrintWriter` 的内容需要手动刷新到目标流中。 2. **编码问题**:字符流和字节流之间的编码不一致可能导致数据丢失。 以下是解决该问题的完整示例代码。 --- ### 示例代码 ```java import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; public class ByteArrayOutputStreamExample { public static void main(String[] args) { // 创建ByteArrayOutputStream对象 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 创建PrintWriter对象,并指定编码为UTF-8 PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8), true); // 写入数据到PrintWriter printWriter.println("Hello, World!"); printWriter.println("这是中文测试"); // 确保所有数据都被刷新到ByteArrayOutputStream中 printWriter.flush(); // 获取字节数组并转换为字符串 String result = byteArrayOutputStream.toString(StandardCharsets.UTF_8); // 输出结果 System.out.println(result); // 关闭资源 printWriter.close(); try { byteArrayOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` --- ### 代码解释 1. **`ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();`** 创建一个 `ByteArrayOutputStream` 对象,用于存储字节数据。 2. **`PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8), true);`** - 使用 `OutputStreamWriter` 将字符流转换为字节流,并指定编码为 UTF-8。 - `PrintWriter` 的第二个参数设置为 `true`,表示自动刷新模式。每次调用 `println` 或 `printf` 后,数据会自动写入底层流。 3. **`printWriter.println("Hello, World!");`** 向 `PrintWriter` 写入一行文本。 4. **`printWriter.flush();`** 手动刷新 `PrintWriter` 中的内容到 `ByteArrayOutputStream`。如果启用了自动刷新(构造函数中的第二个参数为 `true`),这一步可以省略。 5. **`String result = byteArrayOutputStream.toString(StandardCharsets.UTF_8);`** 将 `ByteArrayOutputStream` 中的字节数据转换为字符串,并指定编码为 UTF-8。 6. **`System.out.println(result);`** 输出最终结果。 7. **关闭资源** 使用 `close()` 方法关闭 `PrintWriter` 和 `ByteArrayOutputStream`,释放资源。 --- ### 如果输出为空的原因分析 1. **未刷新流**:如果没有调用 `flush()` 或启用自动刷新,`PrintWriter` 中的数据不会被写入到 `ByteArrayOutputStream`。 2. **编码不匹配**:如果 `OutputStreamWriter` 和 `toString()` 方法使用的编码不一致,可能会导致数据丢失或乱码。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值