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.

Другие вопросы по тегам