2009-11-04 2 views
3

У меня есть кусок испытательного оборудования, из которого я могу прочитать данные с использованием InputStream, который вкрапляет байты и символы (организованные в линию), например:Java: пересыпая байты и символы

TEST1 
TEST2 
500 
{500 binary bytes follows here} 
TEST3 
TEST4 
600 
{600 binary bytes follows here} 

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

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

Редактировать: альтернатива, просто используя InputStream всюду и вынуждая преобразовывать байты-> символы и искать новые строки, похоже, что это определенно будет работать, но также будет настоящей болью.

+0

Какая кодировка кодируется в тексте? – McDowell

+0

Я не совсем уверен. Это похоже на ASCII, но я не знаю, имеет ли он возможность содержать другие вещи. –

+0

Это IEEE 488.2, но опять же, я не уверен, что это гарантировано ASCII. –

ответ

1

При использовании BufferedReader вы можете просто использовать String#getBytes(), чтобы получить байты из строки String. Не забывайте учитывать кодировку символов. Я рекомендую использовать UTF-8 все время.

Только для вашей информации: с другой стороны, если у вас есть только байты, и вы хотите создать символы, просто используйте new String(bytes). Также не забывайте учитывать кодировку символов здесь.

[Edit] В конце концов, лучше использовать BufferedInputStream и построить байтовый буфер для одной строки (заполнить до тех пор, пока байт не соответствует строке) и проверить, соответствует ли представление символа его начала одному из предопределенных строки.

+2

, который помогает, но что, если строка байтов плохо интерпретируется bufferedReader? например если BufferedReader использует 2-байтные (16-разрядные) символы, и есть нечетное количество байтов, и BufferedReader зависает, потому что он пытается прочитать лишний байт, который не существует? –

+0

Затем создайте InputStreamReader с правильной кодировкой, например UTF-8. – BalusC

+2

Это действительно страшная идея. Не обрабатывайте произвольные байты как данные UTF-8 (сколько данных считывается и преобразуется в UTF-8 с помощью буфера). Не все байтовые значения действительны UTF-8. Если последовательность байтов недействительна, она будет беззвучно заменена. – McDowell

0

Я думаю, что я собираюсь взять удар по использованию java.nio.ByteBuffer и ByteBuffer.asCharBuffer, который выглядит многообещающим. Все равно придется искать новые строки вручную, но, по крайней мере, похоже, что он правильно обрабатывает перевод символов.

+0

ick, я не думаю, что это сработает, так как позиции и предельные значения не отражены между двумя буферами. убирайся. –

+0

Было бы все равно, если бы ваш текст кодировался в формате UTF-16. – McDowell

-1

У BufferedReader есть read(char[] cbuf, int off, int len) Вы не можете использовать это, конвертировать символы в байты и обернуть его ByteArrayInputStream?

EDIT: зачем кому-то делать это? Прокомментируйте пожалуйста. Это отлично работает:

ByteArrayOutputStream bos = new ByteArrayOutputStream(); 

    try { 
     bos.write("TEST1\n".getBytes()); 
     bos.write("10\n".getBytes()); 
     for (int i = 0; i < 10; i++) 
      bos.write(i); 
     bos.write("TEST2\n".getBytes()); 
     bos.write("1\n".getBytes()); 
     bos.write(25); 

     ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 
     BufferedReader br = new BufferedReader(new InputStreamReader(bis)); 

     while (br.ready()) { 
      String s = br.readLine(); 
      String num = br.readLine(); 
      int len = Integer.valueOf(num); 
      System.out.println(s + ", reading " + len + " bytes"); 
      char[] cbuf = new char[len]; 
      br.read(cbuf); 
      byte[] bbuf = new byte[len]; 
      for (int i = 0; i < len; i++) 
       bbuf[i] = (byte) cbuf[i]; 
      for (byte b: bbuf) 
       System.out.print(b + " "); 
      System.out.println(); 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

Выход:

TEST1, reading 10 bytes 
0 1 2 3 4 5 6 7 8 9 
TEST2, reading 1 bytes 
25 
+0

'чтения' классы преобразуют данные. Все данные здесь проходят через «BufferedReader». В системе Ubuntu (кодировка UTF-8 по умолчанию) произвольная последовательность байтов 'c2 a3 c2 a3' станет символьными значениями' 00A3 00A3' (4 байта составляет 2 символа). Декодирование байтов '80 81' с помощью окна-1252 (по умолчанию на английском языке Windows) становится символьным значением' 20ac fffd'. Даже декодирование, поскольку US-ASCII потеряет данные, поскольку ASCII использует только первые 7 бит октета. – McDowell

+0

Хорошо, я вижу проблему. Выбор windows1252 в качестве кодировки искажает байты от 128 до 160. –

+0

Упс - в интересах точности, я должен был сказать, что 'InputStreamReader' преобразует данные. @Pilgrim - в значительной степени - данные преобразуются в UTF-16; и значения окон-1252 выше 127 имеют разные значения в UTF-16. – McDowell

0

Посмотрите на исходный код LineNumberInputStream. Сам класс устарел, но похоже, что это именно то, что вам нужно здесь.

Этот класс позволяет читать строки байтов, а затем использовать обычные методы чтения InputStream.

Если вы не хотите перетаскивать устаревший код в свою систему, просто заимствуйте у него некоторые детали реализации.

+0

«Устаревший. Этот класс неправильно предполагает, что байты адекватно представляют символы». –

0

У меня нет хорошего ответа для общего случая (так что другие ответы приветствуются), но если я предполагаю, что ввод ISO-8859-1 (8-битные символы), для меня работает следующее, хотя я думаю литье 8-битного байта, так как char не обязательно гарантирует ISO-8859-1.

Существующий InputStream.read (byte [] b) и InputStream.read (byte [] b, int ofs, int len) позволяет мне читать байты.

public class OctetCharStream extends InputStream { 
    final private InputStream in; 
    static final private String charSet = "ISO-8859-1"; 

    public OctetCharStream(InputStream in) 
    { 
     this.in=in; 
    } 

    @Override public int read() throws IOException { 
     return this.in.read(); 
    } 

    public String readLine() throws IOException 
    { 
     StringBuilder sb = new StringBuilder(); 
     while (true) 
     { 
      /* 
      * cast from byte to char: 
      * fine for 8-byte character sets 
      * but not good in general 
      */ 
      char c = (char) read(); 
      if (c == '\n') 
       break;   
      sb.append(c); 
     } 
     return sb.toString(); 
    } 
    public String readCharacters(int n) throws IOException 
    { 
     byte[] b = new byte[n]; 
     int i = read(b); 
     String s = new String(b, 0, i, charSet); 
     return s; 
    } 
} 

Интересно, что когда я попытался использовать InputStreamReader в одиночку, а не оборачивать BufferedReader вокруг него, то InputStreamReader.read() еще буферы до некоторой степени, читая «жадностью» более чем один символ, даже если вы просто хотите, чтобы вытащить вне один знак. Поэтому я не мог использовать InputStreamReader для обертывания InputStream и попытаться использовать как InputStream, так и InputStreamReader для чтения байтов/символов, в соответствии с которыми мне нужно в данный момент.

1

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

interface MixedProcessor { 
    void processBinaryData(byte[] bytes, int off, int len); 
    void processText(String line); 
} 

Затем есть еще один класс «разделитель», что:

  • решает, какие секции ввода являются текстовыми и которые являются бинарными, и передает их в соответствующий метод процессора
  • преобразует байты в символы при необходимости (с помощью CharsetDecoder)

класс сплиттера может выглядеть следующим образом:

class Splitter { 
    public Splitter(Charset charset) { /* ... */ } 
    public void readFully(InputStream is, MixedProcessor processor) throws IOException { /* ... */ } 
} 
+0

hmm. интересно. это похоже на SAX вместо StAX для XML (push-processing vs. pull-processing). Обратные вызовы добавили бы значительные сложности для моего конкретного приложения, но в целом это может быть полезно. –

Смежные вопросы