【问题标题】:Java byte to String encoding problem on LinuxLinux上的Java字节到字符串编码问题
【发布时间】:2011-10-24 22:20:05
【问题描述】:

我正在实现一个这样工作的软件:

我有一个 Linux 服务器,它运行一个输出文本的 vt100 终端应用程序。 我的程序远程登录服务器并将文本的位读取/解析为相关数据。 相关数据被发送到由网络服务器运行的小型客户端,该客户端在 HTML 页面上输出数据。

我的问题是某些特殊字符(例如“åäö”)被输出为问号(经典)。

背景:
我的程序使用 Apache Commons TelnetClient 读取字节流。字节流被转换为字符串,然后相关位被子串化并与分隔符一起放回。在此之后,新字符串被转换回字节数组并使用 Socket 发送到由网络服务器运行的客户端。该客户端从接收到的字节创建一个字符串,并将其打印在标准输出上,网络服务器从中读取并输出 HTML。

第 1 步: byte[] --> String --> byte[] --> [发送给客户端]

第二步: byte[] --> String --> [打印输出]

问题:
当我在 Windows 上运行我的 Java 程序时,所有字符,包括“åäö”,都会在生成的 HTML 页面上正确输出。但是,如果我在 Linux 上运行该程序,所有特殊字符都会转换为“?”(问号)。

网络服务器和客户端当前正在 Windows 上运行(第 2 步)。

代码:
该程序基本上是这样工作的:

我的程序:

byte[] data = telnetClient.readData() // Assume method works and returns a byte[] array of text.

// I have my reasons to append the characters one at a time using a StringBuffer.
StringBuffer buf = new StringBuffer();
for (byte b : data) {
    buf.append((char) (b & 0xFF));
}

String text = buf.toString();

// ...
// Relevant bits are substring'ed and put back into the String.
// ...

ServerSocket serverSocket = new ServerSocket(...);
Socket socket = serverSocket.accept();
serverSocket.close();

socket.getOutputStream.write(text.getBytes());
socket.getOutputStream.flush();

webserver运行的客户端:

Socket socket = new Socket(...);

byte[] data = readData(socket); // Assume this reads the bytes correctly.

String output = new String(data);

System.out.println(output);

假设读写之间的同步工作正常。

想法:
我尝试了不同的编码和解码字节数组的方法,但没有结果。我对字符集编码问题有点陌生,想得到一些指示。 Windows“WINDOWS 1252”中的默认字符集似乎让特殊字符一直通过服务器到网络服务器,但是在Linux计算机上运行时,默认字符集是不同的。我尝试运行“Charset.defaultCharset().forName()”,它显示我的 Linux 计算机设置为“US-ASCII”。我以为Linux默认是“UTF-8”?

我应该如何让我的程序在 Linux 上运行?

【问题讨论】:

标签: java linux character-encoding apache-commons


【解决方案1】:

依赖平台默认编码通常是个坏主意,尤其是对于网络通信协议。

new String()String.getBytes() 都被重载以允许您指定编码。由于您控制编码和解码,因此只需使用 UTF-8(硬编码)。

还要检查您的代码是否使用了FileInputStreamFileOutputStreamInputStreamReaderOutputStreamWriter,所有这些都可能依赖于平台默认编码(前两个,排他性的,这使得它们非常无用)。

【讨论】:

  • 我应该如何使用 StringBuffer 进行解码? buf.append(new String(new byte[] { b }, "UTF-8"))?但是否则我应该在整个程序(包括客户端)中始终使用 UTF-8 进行解码和编码?
  • @snipes83:删除 StringBuffer 的东西。这是一个毫无意义且容易出错的复杂问题,并且需要更复杂的逻辑才能为 UTF-8 工作,因为它将为 ASCII 以外的字符使用多个字节。或者解释你想要这样做的原因,我们可能会找到更好的解决方案来实现你真正想要的。否则,是的,到处使用 UTF-8。并且尽量避免字符串和字节之间的转换。
  • 使用 StringBuffer 的原因是因为我正在插入一个 VT100 终端框架,它有 80 列宽和 24 行高。为了跟踪每个字符的格式(颜色、粗体、反转背景),我使用了一个单独的相同矩阵来保存这些信息。创建字符串时,我根据格式矩阵检查矩阵中的每个原始字节,并在 XML 格式标记之前和之后创建一个字符。这很复杂,我宁愿不做任何更改。
  • @snipes83:嗯,在这种情况下,您需要考虑第二种编码方式——telnet 客户端使用什么编码方式?如果它是 ISO-8859-1,那么您当前的代码将不加修改地工作,但您应该记录这个假设,因为它非常隐含(事实上,Unicode 的前 256 个代码点向后兼容 ISO-8859-1)。最好先将您从 telnet 客户端获得的字节一次性转换为字符串并显式使用编码,然后对该字符串中的字符执行格式化矩阵。
  • 我应该提一下,如果我使用“java -jar -Dfile.encoding=ISO-8859-1 myprogram.jar”,它在 Linux 上可以正常工作并输出所有特殊的字符正确。然而,这是一个非常糟糕的解决方案,我想在代码中正确编码。上述解决方案意味着 TelnetClient 正在输出由某种 ISO-8859-1 兼容字符集编码的数据?
【解决方案2】:

String(byte[] bytes, String encoding) 是你的朋友。只需将所有原始字节读入字节缓冲区并使用此构造函数将字节解码 成 Java 字符串。 (或:转码为UTF-16,内部字符编码)

getBytes(String encoding) 方法会将字符串编码为字节。

【讨论】:

    【解决方案3】:

    关键细节是telnetClient.readData()返回的数据的编码是什么?听起来像是windows-1252。考虑到这一点,您有几个选择。您可以将所有String 操作的编码显式设置为windows-1252

    text.getBytes("windows-1252");
    
    String output = new String(data, "windows-1252");
    

    或者您可以使用 java.nio.charset.Charset 将 telnet 数据转换为不特定于平台的数据,例如 UTF-8,遵循以下示例:Converting UTF-8 to ISO-8859-1 in Java - how to keep it as single byte -- 仍然在 String 操作中明确设置字符集。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-14
      • 1970-01-01
      • 2013-08-22
      • 1970-01-01
      相关资源
      最近更新 更多