3

这是我的问题;我有一个已转换为字节数组的 InputStream,但在运行时我不知道 InputStream 的字符集。我最初的想法是在 UTF-8 中做所有事情,但我发现编码为 ISO-8859-1 并具有外来字符的流存在奇怪的问题。(那些疯狂的瑞典人)

这是有问题的代码:

IOUtils.toString(inputstream, "utf-8")
// Fails on iso8859-1 foreign characters

为了模拟这一点,我有:

new String("\u00F6")
// Returns ö as expected, since the default encoding is UTF-8

new String("\u00F6".getBytes("utf-8"), "utf-8")
// Also returns ö as expected.

new String("\u00F6".getBytes("iso-8859-1"), "utf-8")
// Returns \uffff, the unknown character

我错过了什么?

4

2 回答 2

4

并非所有字节序列都是有效的 UTF-8 字符。某些字节序列无效,并且通过将其转换\u00F6为等效的 latin-1 字符,您生成了无效的 UTF-8 内容。

于 2013-02-06T03:49:12.843 回答
1

您应该让数据源告诉您编码,但如果这不能发生,您需要拒绝它或猜测编码(如果它不是 UTF-8)。

对于西方语言,猜测 ISO-8859-1 如果不是 UTF-8 可能大部分时间都可以工作:

ByteBuffer bytes = ByteBuffer.wrap(IOUtils.toByteArray(inputstream));
CharBuffer chars; 

try {
    try {
        chars = Charset.forName("UTF-8").newDecoder().decode(bytes);
    } catch (MalformedInputException e) {
        throw new RuntimeException(e);
    } catch (UnmappableCharacterException e) {
        throw new RuntimeException(e);
    } catch (CharacterCodingException e) {
        throw new RuntimeException(e);
    }
} catch (RuntimeException e) {
    chars = Charset.forName("ISO-8859-1").newDecoder().decode(bytes);
} 
System.out.println(chars.toString());

所有这些样板都是为了获取编码异常并能够多次读取相同的数据。

如果不是 UTF-8,您还可以使用Mozilla Chardet ,它使用更复杂的启发式方法来确定编码。但它并不完美,例如我记得它在 Windows-1252 中将芬兰语文本检测为希伯来语 Windows-1255。

另请注意,任意二进制数据在 ISO-8859-1 中是有效的,因此这就是您首先检测 UTF-8 的原因(如果它毫无例外地通过 UTF-8,它就是 UTF-8),这就是为什么您在 ISO-8859-1 之后无法尝试检测任何其他内容。

于 2013-02-06T10:35:33.767 回答