2014-01-02 1 views
2

У меня возникли трудности с перевариванием некоторых понятий в классах Java IO. Например, существуют два типа потоков: байт и символ. Байтовые потоки, как я понимаю, читают байты по байт.

1. Если символ в java хранится как 16-битный (двухбайтовый) тип данных, как мне можно точно прочитать символ, скажем, «A», из файла с использованием байтового ориентированного потока ввода, например. FileInputStream?

2. Является ли то, что символы, которые я использовал (в основном между 0 и 122 на диаграмме ascii), хранятся в одном байте двух выделенных байтов?

3. DataInputStream/DataOutputStream позволяет мне читать и записывать двоичные данные, другие входные потоки, такие как FileInputStream/FileOutputStream, позволяют мне точно читать и писать? Я в основном хочу знать, какой поток использовать, когда я хочу выводить данные в виде текста, который я могу прочитать (используя простой текстовый редактор, например блокнот), и когда я хочу, чтобы он был закодирован как необработанные двоичные данные (текст, который выглядит как мусор в блокноте)?Понимание двоичных, байтовых потоков и символов в java

Борясь, чтобы понять концепцию потоков в java и использовать, когда.

+0

символьные потоки сделки с символами, а не байт. Говорить, что символьные потоки «чтение байта в байт» неточно. – davmac

+1

'char' _is_ - 16-разрядный тип данных. Он не хранит символ; Он хранит код UTF-16. Точно один или два кодовых блока UTF-16 содержат кодовую точку UTF-16. Кодовое обозначение обозначает конкретный символ Юникода. Кроме того, вы смотрите на неправильный набор символов. Java обычно использует [Unicode] (http://en.wikipedia.org/wiki/Unicode#Unicode_in_use), хотя некоторые классы потоков по умолчанию используют набор символов по умолчанию для ОС. –

ответ

1

Если символ в Java хранится как тип данных 16bit (два байта), как это возможно для меня точно читать символ, скажем, «A», из файла, используя байт ориентированный вход поток, например. FileInputStream?

Try делает

System.out.println(Integer.toBinaryString('A')); 

, которая печатает бинарное представление символа 'A'. Печатается

1000001 

Поскольку 'A' является char, это на самом деле хранится с 16 битами

00000000 01000001 

Так все, что вам нужно сделать, это прочитать два последовательных байта и использовать их соответствующим образом, чтобы сформировать char. Смотрите, что в действии

ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0b00000000, 0b01000001}); 
System.out.println(buffer.getChar()); 

, который печатает

A 

Что это делает сделать первый byte в массиве и использовать его в качестве первых 8 битов в char и второй byte как последний 8 биты.


DataInputStream/DataOutputStream позволяет мне читать и писать двоичные данные , другие входные потоки, как FileInputStream/FileOutputStream позволяет меня читать и писать, что именно? Я в основном хочу знать, какой поток использовать, когда я хочу выводить данные в виде текста, который я могу прочитать (используя простой текстовый редактор , например блокнот) по сравнению с тем, когда я хочу, чтобы он был закодирован как необработанный двоичный файл (текст, который выглядит как мусор в блокноте)?

Если вы пишете текст или что-то еще, это все бит и байты.Вы можете очень хорошо сделать

"someString".getBytes() 

и напишите их. Так что это не имеет большого значения. Используйте то, что больше всего отражает то, что вы делаете. Как правило, вы можете обернуть базовый OutputStreamPrintWriter и базовым InputStream с помощью Scanner или BufferedReader.

+0

Мне нравится ваш ответ, это замечательно - ByteBuffer buffer = ByteBuffer.wrap (новый байт [] {0b00000000, 0b01000001}); System.out.println (buffer.getChar()); –

2

В зависимости от формата файла, который вы читаете.

Если файл представляет собой поток ASCII байт, то сделать это:

InputStream is = new FileInputStream(filePath); 
Reader reader = new InputStreamReader(is, "ISO-8859-1"); 

char ch = reader.read(); 

Вы всегда сначала открыть входной поток на байт-ориентированный файл. Затем InputStreamReader преобразует байты в символы. Конечно, в этом случае ISO-8859-1 является отображением от однобайтовых значений до одинаковых значений символов. Очевидно, что другое сопоставление возможно, но ISO-8859-1 бывает таким же, как первые 255 символов набора Unicode, а первые 127 из них совпадают с ASCII.

При написании использование:

OutputStream os = new FileOutputStream(filePath) ; 
Writer w = new OutputStreamWriter(os, "ISO-8859-1"); 

w.write(ch); 

Еще раз, это OutputStreamWriter, который преобразует между символами и байтов надлежащим образом в соответствии с ISO-8859-1 набор символов. Результирующий файл будет иметь один байт на символ.

Вот еще несколько примеров из proper basic stream patterns.

При использовании выше, вы выполнить это:

w.write("AAAA"); 
w.flush(); 
w.close(); 

Полученный файл будет содержать 4 байта со значением 65 в каждом байте. Чтение этого файла при использовании кода вверху приведет к появлению четырех символов «A» в памяти, но в памяти они занимают 16 бит для каждого символа.

Если файл закодирован в другом наборе символов, включая, возможно, несколько байтовых символов, тогда просто используйте правильную кодировку в InputStreamReader/OutputStreamWriter, и правильное преобразование будет происходить при чтении и записи.

UTF-8 не является набором символов, а скорее кодировкой обычных символов Юникода в байтовые последовательности, и получается, что кодировка UTF-8 довольно умна тем, что первые 127 символов символов Юникода отображаются в первые 127 байтовых значений (как одиночные байты). Затем символы> = 128 используют 2 или более байтовых значения в строке, где каждое из этих байтовых значений составляет> = 128. Если вы знаете, что файл ascii использует только «7-битный» ASCII, то UTF-8 будет работать для вас. Для Java в целом UTF-8 - лучшая кодировка, используемая для файла, поскольку она может без ошибок кодировать все возможные значения Java char.

Узнав об этом в потоках очень важно. Я рекомендую вам не пытаться преобразовывать байты в символы любым другим способом. Конечно, это возможно, но это пустая трата усилий, поскольку конверсии в потоках очень надежны и правильны.

(Ухудшается ... на самом деле символ - это 32-битное количество, из которых 20 бит могут быть закодированы в последовательности 16-разрядных значений char с кодировкой UTF-16. Рекомендуем вам игнорировать это на данный момент , но просто имейте в виду, что даже в Java String, состоящем из 16-битных значений char, есть несколько последовательностей с двумя символами.)

+0

Хороший ответ, но ... Java [Charset] (http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html) для ASCII - это «US-ASCII», а не "ISO-8859-1".Я полагаю, вы упомянули «ISO-8859-1», потому что трудно найти пример, где ASCII все еще используется. –

+0

Я понимаю, что вы сказали. Иногда это может быть немного запутанным с таким количеством вариантов, которые вы выбрали для чтения файла. Некоторые вещи я не понимаю полностью, но я хочу прочитать еще несколько материалов, основанных на вашем ответе и других ответах, и поболтать с каким-то кодом и посмотреть, что я придумал. Большое спасибо! – jmreader

+0

@Tom Технически вы правы, но этот ответ не так полезен. ISO-8859-1 должен по существу всегда использоваться вместо этого. US-ASCII определяет только 128 символов. Байт может содержать 256 значений. Что делать с этими другими ценностями? Первые 128 символов ISO-8859-1 точно такие же, как US-ASCII. Если файл содержит только 7-битный ASCII, то оба кодировки будут работать одинаково хорошо; нет преимущества для US-ASCII. Но если появляются значения байтов> 127, то ISO-8859-1 обеспечивает разумную обработку их, * и * ISO-8859-1 является кодировкой по умолчанию в WWW, поэтому многие файлы кодируются таким образом. – AgilePro

1

Прежде чем я попытаюсь ответить на ваш вопрос, есть несколько очень простых вещей для понимания.

  1. На самом нижнем уровне InputStream/OutputStream), все бит и байты. Таким образом, потоки самого низкого уровня имеют дело с необработанными данными, которые являются битами/байтами.
  2. Теперь для перевода необработанных байтов в считываемые символы требуется кодировка символов или Character Set. Короче говоря, кодировка символов - это инструкция (), отображающая отображение от байтов до визуальных символов) для перевода необработанных байтов в считываемые символы из определенного набора (например, UTF-8).

Сейчас подходит на ваши вопросы:

Если символ в Java хранится как тип данных 16bit (два байта), как это возможно для меня, чтобы точно читать символ, скажем, «A» , из файла с использованием байтового ориентированного входного потока, например. FileInputStream?

Для чтения символьных данных, необработанные входные потоки завернуты в символьных потоков, ориентированных, например,

FileInputStream fis = new FileInputStream("test.txt"); 
InputStreamReader isr = new InputStreamReader(fis, "UTF8"); 

Как javadoc говорит InputStreamReaderявляется мостом от потоков байтов до символьных потоков.

Является ли это тем, что символы, которые я использовал (в основном между 0 и 122 на диаграмме ascii), хранятся в одном байте двух выделенных байтов?

Да. Шифр ascii - это подмножество большего набора Unicode, такого как UTF-8.

DataInputStream/DataOutputStream позволяет мне читать и записывать двоичные данные, другие входные потоки, такие как FileInputStream/FileOutputStream, позволяют мне точно читать и писать?

Я думаю, теперь это очевидно, что DataInputStream/DataOutputStream предназначены для символьных данных, тогда как ileInputStream/FileOutputStream предназначены для необработанных данных.

Я в основном хочу знать, какой поток использовать, когда я хочу выводить данные в виде текста, который я могу прочитать (используя простой текстовый редактор, например блокнот) по сравнению с тем, когда я хочу, чтобы он закодирован как необработанные двоичные данные (текст, который выглядит как мусор в блокноте)?

Для текста используйте для чтения/записи (Here is an example)