【问题标题】:Encoding ignored while reading InputStream读取 InputStream 时忽略编码
【发布时间】:2014-10-02 09:40:00
【问题描述】:

我在向 IIS 服务器发出 HTTP 请求的 Java 应用程序中遇到了一些编码问题。

遍历URLConnection 对象的标头,我可以看到以下(相关)标头:

Transfer-Encoding: [chunked]
Content-Encoding: [utf-8]
Content-Type: [text/html; charset=utf-8]

URLConnection.getContentEncoding() 方法返回 utf-8 作为文档编码。

这就是我的 HTTP 请求和流读取的方式:

OutputStreamWriter sw = null;
BufferedReader br = null;
char[] buffer = null;
URL url;
url = new URL(this.URL);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
sw = new OutputStreamWriter(connection.getOutputStream());
sw.write(postData);
sw.flush();
br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF8"));
StringBuilder totalResponse = new StringBuilder();
String line;

while((line = br.readLine()) != null) {
    totalResponse.append(line);
}
buffer = totalResponse.toString().toCharArray();
if (sw != null)
    sw.close();

if (br != null)
    br.close();

return buffer;

但是,服务器发送的以下字符串“ÃÃÃção”被客户端接收为“�����o”。

我做错了什么?

【问题讨论】:

  • 我认为它必须是 UTF-8 而不是 UTF8
  • 感谢@Tirath 的回复。我已将 UTF8 更改为 UTF-8 作为 InputStreamReader 构造函数的参数,但结果是一样的。
  • 您确定您的内容是实际上 UTF-8 编码的吗?标题可以撒谎。你也试过调试totalResponse.toString()吗?如果等于"ÃÃÃção",那么在char[] 上操作时,您的问题可能会更进一步...
  • 谢谢@Mena,我如何实际上验证内容编码。使用:byte[] foo = String.valueOf(totalResponse.toString()).getBytes(); System.out.println(new String(foo, "utf-8")); 得到完全相同的结果。
  • 可能不相关,但您还应该在创建 OutputStreamWriter 时设置显式编码 - 此时您正在以平台上的默认编码发送帖子数据,这可能不是服务器所期望的。

标签: java http encoding inputstreamreader


【解决方案1】:

根据您的 cmets,您正尝试从 IIS 服务器接收 FIX 消息,而 FIX 使用 ASCII。只有一小部分标签支持其他编码,它们必须以特殊方式处理(标准 FIX 规范中的非 ASCII 标签是 349,351,353,355,357,359,361,363,365)。如果存在此类标签,您将获得一个标签 347,其中包含一个指定编码的值(例如 UTF-8),然后每个标签前面都会有一个标签,为您提供即将到来的编码值的长度(对于标签 349,你总是会先得到 348 的整数值)

在您的情况下,服务器似乎正在以其他编码发送自定义标签 10411(10xxx 范围)。按照惯例,前面的标签 10410 应该给你 10411 中值的长度,但它包含“0000”,这可能有其他含义。

请注意,尽管 FIX 消息可读性很强,但仍应将它们视为二进制数据。标签和值大多是 ASCII 字符,但分隔符 (SOH) 是 0x01,如上所述,某些标签可能使用另一种编码进行编码。 IIS 服务应该真正将数据返回为application/octet-stream,以便可以正确接收。尝试将其返回为 text/html 是自找麻烦 :)。

【讨论】:

  • 你是对的。这是一个基于 FIX 的自定义消息协议。将Content-Type 设置为application/octet-stream 具有相同的结果(�����o 代表 ÃÃÃção)。感谢您的回复。
【解决方案2】:

如果服务器真的发送了“UTF-8”的内容编码,那么它非常感到困惑。见http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#header.content-encoding

【讨论】:

  • Content-Encoding 标签是我的错误。现在不包括在内。
【解决方案3】:

为了获得良好的订单,请进行一些更正。

    URLConnection connection = url.openConnection();
    connection.setDoOutput(true);
    connection.connect();
    try (Writer sw = new OutputStreamWriter(connection.getOutputStream(),
                StandardCharsets.UTF_8)) {
        sw.write(postData);
        sw.flush();

        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(connection.getInputStream(),
                StandardCharsets.UTF_8))) {
            StringBuilder totalResponse = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                totalResponse.append(line).append("\r\n");
            }
            return totalResponse.toString().toCharArray();
        } // Close br.
    } // Close sw.

也许:

postData =  ... + "Accept-Charset: utf-8\r\n" + ...;

收到totalResponse.toString(),您应该已经全部正确阅读了。

但是当再次显示时,String/char 再次转换为字节,并且 there 编码失败。例如 System.out.println 不会像使用 Windows 编码那样做。

您可以通过转储字节来测试字符串:

String s = totalResponse.toString();
Logger.getLogger(getClass().getName()).log(Level.INFORMATION, "{0}",
    Arrays.toString(s.getBytes(StandardCharsets.UTF_8)));

在极少数情况下,字体不会包含特殊字符。

【讨论】:

  • 感谢您的回复。结果和以前一样。对于以下字符串:INFO: 8=FIX.4.29=3335=DRCFG10410=000010411=�����o10=000 \0 我得到以下字节:INFO: [56, 61, 70, 73, 88, 46, 52, 46, 50, 1, 57, 61, 51, 51, 1, 51, 53, 61, 68, 82, 67, 70, 71, 1, 49, 48, 52, 49, 48, 61, 48, 48, 48, 48, 1, 49, 48, 52, 49, 49, 61, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 111, 1, 49, 48, 61, 48, 48, 48, 1, 13, 10, 92, 48]
  • 如果你看 411=��,即 48+4(4), 48+1(1), 48+1(1), 61(=) 一个看到重复四个相同多字节序列。事实上U+FFFD,Unicode 替换字符。由于 UTF-8 可以代表所有,在 Unicoce 的早期转换中,例如 UTF-8 到有限编码,这种转换已经完成。绝对是在 IIS 端,除非数据来自客户端的往返。
  • 所以如果我没有理解错,你是在告诉我 IIS 正在以其他字符集发送数据,并在此过程中被转换?
  • IIS 有时会错误地转换为非 UTF-8(引入替换字符),并最终以 UTF-8 交付。作为健全性检查,可能在浏览器中查询相同的内容。
  • 再次感谢!该服务由多个组件(ObjectiveC、C#、JS)使用,唯一存在编码问题的是 JAVA 中的这个。在服务器端,我可以看到消息被正确发送。关于正在发生的事情还有什么想法吗?
【解决方案4】:

您可以尝试将流作为请求属性的一部分,然后在客户端打印出来。将按原样接收请求属性,没有任何编码问题

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-13
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2010-09-22
    • 2015-05-28
    • 1970-01-01
    相关资源
    最近更新 更多