欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

程序员文章站 2024-04-03 18:44:22
在前面的网页中的编码与乱码系列中,曾多次提到使用 servlet 方式构建的动态响应流,不过在那里都是直接使用字节流的方式,不过,更为常见的方式是使用字符流。而在前面,又谈...

在前面的网页中的编码与乱码系列中,曾多次提到使用 servlet 方式构建的动态响应流,不过在那里都是直接使用字节流的方式,不过,更为常见的方式是使用字符流。而在前面,又谈到了 java 字节流与字符流的话题。

有了前面的基础,现在来说下 java servlet 中使用字符流,也即是 printwriter 时的编码与乱码问题。

回顾字节流的情形

先回顾一下,在之前的字节流响应中,我们使用 string.getbytes 方法,然后总是显式传入编码的参数,使它与 meta 中或者 header 的声明一致。比如这样:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

或者这样:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

只要保持了一致,就不用担心发生乱码的问题。

使用 printwriter 字符流,缺省编码

现在假如使用 printwriter 来作为响应呢?比如这样:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

代码中并没有显式传入什么编码的参数,不像 string.getbytes 那样。另一方面,我们知道,字符流最终还是要转换成字节流,可是它到底使用了什么编码呢?是不是 charset.defaultcharset 中的值呢?

就以上述代码为例,假如现在在浏览器中查看,会发现结果是这样的:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

可见 defaultcharset 缺省是 utf-8,前面说过,这其实来自于启动 tomcat server 时所传入的参数 –dfile.encoding:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

但汉字却没有正确输出,可见 printwriter 并没有采用这个缺省值。查看 header 中的响应:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

也没有任何编码的指示。

虽然 meta 中声明是 utf-8,输出的缺省字符集的值也是 utf-8,可是从最终结果不难看出 printwriter 并没有采纳这个值来转换字节流。(实际上它根本不会试图去理解这个)。

看一看它的文档说明,会发现情况有点不一样:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

原来没有指定时,printwriter 不是用 charset.defaultcharset 中的值,而是用 response.getcharacterencoding 方法中所返回的值,而没有指定的话,那个方法其实就返回一个缺省值:iso-8859-1。

再看看 getcharacterencoding 方法:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

可以看到它的值又是来源于显式的 response.setcharacterencoding 或 response.setcontenttype 方法,或者是隐式的 setlocale 方法。(显式的具有更高的优先级)假如没有,就用缺省的 iso-8859-1。

它还提到 rfc 2047 标准 ,打开看看,是关于 mime 中非 ascii 文本的消息头扩展(mime (multipurpose internet mail extensions) part three:  message header extensions for non-ascii text)的。文中有一处提到如果字符集编码缺失,推荐用 iso8859 系列:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

注意这里没有明说是 iso-8859-1,它说的是 iso-8859-*,不过 servlet 最终采用的是 iso-8859-1.

所以现在清楚了,缺省用 iso-8859-1,可以用 getcharacterencoding 得到它的值,不过 iso 不支持中文字符,所以响应流中不能出现中文:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

结果是这样:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

使用 printwriter 字符流,显式指定编码

按照前面说的,可以在 write 之前使用 setcharacterencoding 等方法指定编码:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

这样就 ok 了:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

要注意,这种情况下,response header 中仍然没有 charset 信息,所以要在 meta 中指定。

也可以用 setcontenttype (或前面一直用的 setheader,其实两者是等价的):

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

也能达成同样效果:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

这种情况下,response header 中包含 charset 信息,所以前面的代码中可以省略在 meta 中的声明:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

那么,现在我们明白了,printwriter 的缺省与普通字符流的缺省是不同的,机制有所差别。

使用普通字符流,缺省编码

当然如果你一定要用普通字符流,也是可以的,但最后需要主动 flush:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

这时的缺省就是 charset.defaultcharset 中的值了,这里把它拼在了 meta 和最终的输出中,响应也是正常的:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

结果是 utf-8。跟前面所说的 tomcat server 启动时参数的值一致。

使用普通字符流,显式指定编码

如果不打算用缺省,那就直接指定:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

结果同样是 ok 的:

Java servlet 使用 PrintWriter 时的编码与乱码的示例代码

当然,一般还是建议使用 printwriter 来输出,而即便你一定要用普通字符流,也最好不要用缺省。

那么关于 java servlet 中使用 printwriter 时的编码与乱码问题就介绍到这里。本文中的示例代码见:servlet-printwriter_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。