Scala - 从ISO-8859-1转换到UTF-8会导致外语字符变得奇怪

3
这是我的问题:我有一个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

我错过了什么?

2
如果您不知道InputStream中编码的(表面上的)字符的编码方式,那么您就无法将其转换为字符。就是这么简单。而且...为什么您会期望将编码转换为ISO-8859-1,然后从UTF-8解码任意字符呢? - Randall Schulz
Nit: new String("\u00F6") 的值如预期一样,并不涉及编码。 - user166390
1
在运行时确定编码方式是 Content-Type 标头及其相应的 charset 参数存在的原因。 - Kristian Domagala
1
这不仅是一个瑞典字母,还是一个德语变音符号。 :) - Madoc
为了更加清楚,引起问题的是 new String("\u00F6".getBytes("iso-8859-1"), "utf-8") 中的 "utf-8" 参数 - 调用 System.out.println(new String("\u00F6".getBytes("iso-8859-1"))); 将非常好地打印出 ö. - Mr_and_Mrs_D
2个回答

4

并非所有的字节序列都是有效的UTF-8字符。有些字节序列是无效的,将\u00F6转换为其相应的Latin-1字符会产生一些无效的UTF-8字符。


1
你应该拥有数据源告诉你编码方式,但如果无法获得,你需要拒绝它或者猜测编码方式是否为UTF-8。
对于西方语言,如果不是UTF-8,则猜测ISO-8859-1可能大部分时间都能起到作用。
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());

所有这些样板都是为了避免编码异常并能够多次读取相同的数据。
您还可以使用 Mozilla Chardet,它使用更复杂的启发式算法来确定编码(如果不是UTF-8)。但它并不完美,例如我记得它将Windows-1252中的芬兰文本检测为希伯来语Windows-1255。
此外,请注意,任意二进制数据在ISO-8859-1中是有效的,因此这就是为什么您首先要检测UTF-8(如果它通过UTF-8而没有异常,则极有可能是UTF-8),并且这也是为什么您不能尝试在ISO-8859-1之后检测任何其他内容的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接