用Java将UTF-8转换为ISO-8859-1

时间:2009-08-13 19:08:12

标签: java utf-8 character-encoding iso-8859-1

我正在阅读XML文档(UTF-8)并最终使用ISO-8859-1在网页上显示内容。正如预期的那样,有一些字符无法正确显示,例如(它们显示为?)。

是否可以将这些字符从UTF-8转换为ISO-8859-1?

以下是我为此尝试编写的一段代码:

BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
StringBuilder sb = new StringBuilder();

String line = null;
while ((line = br.readLine()) != null) {
  sb.append(line);
}
br.close();

byte[] latin1 = sb.toString().getBytes("ISO-8859-1");

return new String(latin1);

我不太确定会出现什么问题,但我相信它是readLine()导致的悲痛(因为字符串是Java / UTF-16编码的?)。我尝试的另一个变体是用

替换latin1
byte[] latin1 = new String(sb.toString().getBytes("UTF-8")).getBytes("ISO-8859-1");

我已经阅读过关于这个主题的帖子,我正在学习。在此先感谢您的帮助。

4 个答案:

答案 0 :(得分:14)

我不确定标准库中是否存在执行此操作的规范化例程。我不认为“智能”引号的转换是由标准Unicode normalizer例程处理的 - 但是不要引用我。

聪明的做法是转储ISO-8859-1并开始使用UTF-8。也就是说,可以将任何通常允许的Unicode代码点编码为编码为ISO-8859-1的HTML页面。您可以使用escape sequences对其进行编码,如下所示:

public final class HtmlEncoder {
  private HtmlEncoder() {}

  public static <T extends Appendable> T escapeNonLatin(CharSequence sequence,
      T out) throws java.io.IOException {
    for (int i = 0; i < sequence.length(); i++) {
      char ch = sequence.charAt(i);
      if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.BASIC_LATIN) {
        out.append(ch);
      } else {
        int codepoint = Character.codePointAt(sequence, i);
        // handle supplementary range chars
        i += Character.charCount(codepoint) - 1;
        // emit entity
        out.append("&#x");
        out.append(Integer.toHexString(codepoint));
        out.append(";");
      }
    }
    return out;
  }
}

使用示例:

String foo = "This is Cyrillic Ya: \u044F\n"
    + "This is fraktur G: \uD835\uDD0A\n" + "This is a smart quote: \u201C";

StringBuilder sb = HtmlEncoder.escapeNonLatin(foo, new StringBuilder());
System.out.println(sb.toString());

在上面,字符LEFT DOUBLE QUOTATION MARK(U+201C )被编码为&amp;#x201C;。同样编码了几个其他任意代码点。

需要注意这种方法。如果您的文本需要针对HTML进行转义,则需要在上述代码或“&”符号被转义之前完成。

答案 1 :(得分:4)

根据您的默认编码,以下行可能会导致问题,

byte[] latin1 = sb.toString().getBytes("ISO-8859-1");

return new String(latin1);

在Java中,String / Char始终为UTF-16BE。只有在将字符转换为字节时才会涉及不同的编码。假设您的默认编码是UTF-8,latin1缓冲区被视为UTF-8,而某些Latin-1序列可能会形成无效的UTF-8序列,您将得到?。

答案 2 :(得分:2)

使用Java 8,可以像这样简化McDowell's answer(同时保留对代理对的正确处理):

public final class HtmlEncoder {
    private HtmlEncoder() {
    }

    public static <T extends Appendable> T escapeNonLatin(CharSequence sequence,
                                                          T out) throws java.io.IOException {
        for (PrimitiveIterator.OfInt iterator = sequence.codePoints().iterator(); iterator.hasNext(); ) {
            int codePoint = iterator.nextInt();
            if (Character.UnicodeBlock.of(codePoint) == Character.UnicodeBlock.BASIC_LATIN) {
                out.append((char) codePoint);
            } else {
                out.append("&#x");
                out.append(Integer.toHexString(codePoint));
                out.append(";");
            }
        }
        return out;
    }
}

答案 3 :(得分:1)

当您实例化String对象时,需要指明要使用的编码。

所以替换:

return new String(latin1);

通过

return new String(latin1, "ISO-8859-1");