Scala - Переход от ISO-8859-1 к UTF-8 дает странный характер
Вот моя проблема; У меня есть 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
Что мне не хватает?
2 ответа
У вас должен быть источник данных, сообщающий вам кодировку, но если этого не произойдет, вам нужно либо отклонить ее, либо угадать кодировку, если это не 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());
Все эти шаблоны предназначены для получения исключений кодирования и возможности считывать одни и те же данные несколько раз.
Вы также можете использовать Mozilla Chardet, которая использует более сложную эвристику для определения кодировки, если это не UTF-8. Но он не идеален, например, я вспоминаю, что обнаруживал финский текст в Windows-1252 как иврит Windows-1255.
Также обратите внимание, что произвольные двоичные данные действительны в ISO-8859-1, поэтому вы сначала обнаруживаете UTF-8 (это очень похоже на то, что если он проходит UTF-8 без исключений, это UTF-8), и именно поэтому вы не может попытаться обнаружить что-либо еще после ISO-8859-1.
Не все последовательности байтов являются допустимыми символами UTF-8. Некоторые последовательности байтов недопустимы, и путем преобразования \u00F6
в его эквивалент латинского символа 1 вы создали что-то недопустимое UTF-8.