Чтение как текстовых, так и двоичных данных из InputStream

Я пытаюсь прочитать данные из двоичного потока, части которого должны анализироваться как UTF-8.

Использование InputStream непосредственно для двоичных данных и InputStreamReader поверх него для текста UTF-8 не работает, поскольку считыватель будет читать вперед и искажать последующие двоичные данные, даже если ему будет сказано прочитать максимум n символов.

Я понимаю, что этот вопрос очень похож на Чтение из InputStream в нескольких форматах, но предложенное там решение специфично для потоков HTTP, что мне не помогает.

Я думал просто прочитать все как двоичные данные и впоследствии преобразовать соответствующие фрагменты в текст. Но у меня есть только информация о длине символьных данных в символах, а не в байтах. Таким образом, мне нужна вещь, которая читает символы из потока, чтобы знать кодировку.

Есть ли способ указать InputStreamReader не читать вперед дальше, чем это необходимо для чтения заданного количества символов? Или есть ридер, который поддерживает как бинарные данные, так и текст с кодировкой и может переключаться между этими режимами на лету?


person tajmahal    schedule 30.06.2011    source источник


Ответы (2)


Сначала вам нужно прочитать двоичные части. Если вы распознаете часть байтов, требующих декодирования UTF-8, вам необходимо извлечь эти байты и декодировать их.

DataInputStream dis = 
// read a binary type.
int num = dis.readInt();
int len = dis.readUnsignedShort();
// read a UTF-8 portion.
byte[] bytes = new byte[len];
dis.readFully(bytes);
String text = new String(bytes, "UTF-8");
// read some binary
double d = dis.readDouble();
person Peter Lawrey    schedule 30.06.2011
comment
Проблема в том, что в UTF8 количество байтов может отличаться от количества символов. Поэтому мне нужно было бы узнать количество многобайтовых символов в строке, прочитать больше байтов и снова преобразовать, и делать это снова и снова, пока числа не совпадут. - person tajmahal; 30.06.2011
comment
Я бы сказал, что ваш формат не очень легко декодировать, и я бы исправил его, если бы вы могли. Однако вы можете самостоятельно разобрать UTF-8, если знаете количество символов. (Но отправка фактического количества байтов была бы намного проще) - person Peter Lawrey; 30.06.2011
comment
Другой подход заключается в чтении большего количества данных, чем необходимо. Возьмите ожидаемое количество символов, например. substring() и преобразовать в UTF-8, чтобы определить длину. Используйте mark() и reset() и прочитайте длину, которую вы теперь знаете. (Это работает только в том случае, если кодировка UTF-8 точно такая же: | например, нулевой байт \0 кодируется двумя разными способами (как и другие символы). - person Peter Lawrey; 30.06.2011
comment
Эмпирическое правило заключается в том, что если вам нужно усложнить кодирование или декодирование, усложните кодирование и упростите декодирование. - person Peter Lawrey; 30.06.2011
comment
Хорошо, я решил изменить формат, так как это действительно казалось самым простым способом. - person tajmahal; 30.06.2011

Я думаю, что просто не стоит использовать StreamReader. Читатели имеют дело с текстом, но вы имеете дело с текстом и двоичными данными вместе.

Выхода нет. Вы должны читать двоичные буферы и интерпретировать свой формат самостоятельно, т.е. находить положение байтов извлечения текста и преобразовывать их в строку.

Чтобы упростить эту задачу, я бы порекомендовал вам создать свой собственный класс (скажем, ProtocolRecord). Он должен быть Serializable. Он будет содержать все ваши поля. Теперь у вас есть 2 варианта:

(1) простой - используйте механизм сериализации Java. В этом случае вам просто нужно обернуть свой поток DataInputStream для чтения и DataOutputStream для записи, а затем прочитать/записать свои объекты. Недостатком этого подхода является то, что вы не можете контролировать свой протокол.

(2) реализовать методы readObject() и writeObject() самостоятельно. Теперь используйте DataInputStream и DataOutputStream, как описано выше. В этом случае вам нужно реализовать протокол сериализации, но, по крайней мере, он инкапсулирован в ваш класс.

Он думает, что DataInputStream — это то, что вам нужно.

person AlexR    schedule 30.06.2011