Почему DataOutputStream.writeChars(str) и String(byte[]) не используют одну и ту же кодировку?

Я пишу несколько процедур маршалинга/демаршалинга для проекта класса и немного озадачен поведением Java по умолчанию в этом случае. Вот мои «наивные» подпрограммы для записи и чтения строк в потоки байтов и из них:

protected static void write(DataOutputStream dout, String str)
        throws IOException{
    dout.writeInt(str.length());
    dout.writeChars(str);
}

protected static String readString(DataInputStream din)
        throws IOException{
    int strLength = 2*din.readInt(); // b/c there are two bytes per char
    byte[] stringHolder = new byte[strLength];
    din.read(stringHolder);
    return new String(stringHolder);
}

К сожалению, это просто не работает; символы записываются в формате UTF-16 по умолчанию, но String(byte[]), похоже, предполагает, что каждый байт будет содержать символ, и, поскольку все символы ASCII начинаются с 0 байта в UTF-16, конструктор просто отказывается и возвращает пустой строки. Решение состоит в том, чтобы изменить readString, чтобы указать, что он должен использовать кодировку UTF-16:

protected static String readString(DataInputStream din)
        throws IOException{
    int strLength = 2*din.readInt();
    byte[] stringHolder = new byte[strLength];
    din.read(stringHolder);
    return new String(stringHolder, "UTF-16");
}

У меня вопрос, зачем это нужно? Поскольку Java по умолчанию использует UTF-16 для строк, почему бы не предположить, что UTF-16 используется при чтении символов из байтов? Или, в качестве альтернативы, почему бы ему просто не кодировать символы как байты по умолчанию? Короче говоря, почему поведение по умолчанию метода writeChars() и конструктора String(byte[]) не параллельно друг другу?


person Kyle Strand    schedule 17.02.2013    source источник
comment
Как насчет использования DataOutputStream.writeUTF() и DataInputStream.readUTF()?   -  person Boris the Spider    schedule 18.02.2013
comment
....вау, так удобнее. Спасибо. Мне все еще любопытно, почему мой метод не работает должным образом, но я думаю, что вместо этого я переключусь на использование этих методов.   -  person Kyle Strand    schedule 18.02.2013
comment
Я опубликовал полное объяснение, надеюсь, что это прояснит ситуацию.   -  person Boris the Spider    schedule 18.02.2013


Ответы (2)


Проблема в том, что вы пишете, используя базовый char[], который по сути является byte[], представляющим представление строки UTF-16, см. javadoc.
Затем вы читаете с помощью конструктора String(byte[] bytes), который в вашем случае предназначен для чтения данных, закодированных с помощью системной кодировки по умолчанию. предположительно это UTF-8.
Вы должны быть последовательными, на самом деле функции DataOutputStream.writeUTF() и DataInputStream.readUTF() разработаны специально для этого.
Если вы хотите использовать базовый byte[] по какой-то причине, вы можете получить UTF- 8 легко представить String с помощью String.getBytes("UTF-8"), опять же, см. javadoc.
Чтобы упростить задачу, вы можете просто использовать ObjectOutputStream и ObjectInputStream, и это будет сериализовать фактический String в поток, а не только его представление char[].

person Boris the Spider    schedule 18.02.2013
comment
Конструктор String(byte[]) не обязательно использует UTF-8, но кодировку платформы по умолчанию. - person Louis Wasserman; 18.02.2013

Лучше думать, что Java не использует никакой кодировки своих символов. Его строки представляют собой просто необработанное 16-битное значение char, такое же, как UTF16. Причина, по которой «другие» методы по умолчанию используют системную кодировку, заключается в том, что разные платформы используют разные кодировки по умолчанию. Например, не имеет смысла записывать кодировку UTF8, содержащую частичные символы ascii, на мейнфрейм, использующий EBDCDIC (sp).

person mP.    schedule 18.02.2013