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

Что мне не хватает?


person user2045359    schedule 06.02.2013    source источник
comment
Если вы не знаете кодировку (мнимых) символов, закодированных в InputStream, вы не можете превратить их в символы. Это так просто. И... Почему вы ожидаете, что кодирование в ISO-8859-1, а затем декодирование из UTF-8 будет работать для произвольных символов?   -  person Randall Schulz    schedule 06.02.2013
comment
Нит: new String("\u00F6"), имеющее ожидаемое значение, не имеет ничего общего с кодировкой..   -  person    schedule 06.02.2013
comment
Определение кодировки во время выполнения является причиной существования Content-Type заголовков и соответствующих им charset параметров.   -  person Kristian Domagala    schedule 06.02.2013
comment
Это не только шведская буква, но и немецкий умлаут. :)   -  person Madoc    schedule 06.02.2013
comment
Чтобы быть более ясным, именно аргумент "utf-8"new String("\u00F6".getBytes("iso-8859-1"), "utf-8")) вызывает проблему - вызов System.out.println(new String("\u00F6".getBytes("iso-8859-1"))); очень хорошо напечатал бы ö   -  person Mr_and_Mrs_D    schedule 11.04.2013


Ответы (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.

person Esailija    schedule 06.02.2013

Не все последовательности байтов являются допустимыми символами UTF-8. Некоторые последовательности байтов недействительны, и, преобразовав \u00F6 в его эквивалентный символ латинской 1, вы создали что-то, что не является допустимым UTF-8.

person Daniel C. Sobral    schedule 06.02.2013