2014-03-05 3 views
1

Я хочу читать и обрабатывать поток String, содержащий одиночные или несколько команд в одной строке.Чтение и обработка потоков

В настоящее время я использую InputStream in = socket.getInputStream(); для моего входного потока.

Кроме того, для обработки на вход типовой Темы:

public void run() { 
    String input = ""; 
    try { 
     int data = 0; 
     while (!isInterrupted()) { 
      while ((data = in.read()) != -1 && !isInterrupted()) { 
       input += Integer.toHexString(data); 
       handleInput(input); 
      } 
      try { 
       sleep(500); 
      } catch (InterruptedException e) { 
       break; 
      } 
     } 
     socket.close(); 
     return; 
    } catch (IOException e) { 
     main.log("Connection lost..."); 
     main.log(e.toString()); 
     main.stopBTCommunication(); 
     main.startBTServer(); 
    } 
} 

handleInput() предназначен для обработки любой строки, данной ему и ответ правильно. Проблема с этой реализацией будет заключаться в том, что handleInput() вызывается с каждым байтом, считанным с in.read(). Я знаю, что я мог бы использовать BufferedReader.readLine(), но для этого требуется, чтобы каждая команда incomming добавила к ней "\n", что НЕ имеет значения и не может быть изменено.

Я знаю, что

 while (!isInterrupted()) { 
      while ((data = in.read()) != -1 && !isInterrupted()) { 

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

EDIT:

Так что, в основном, мне нужен неблокирующий read().

EDIT2:

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

  • Выберите команду: "s"
  • ReadLine Команда: "rlXX", когда X представляет шестнадцатеричное число
  • WriteLine Команда: «wlXXSSSSSSSS», в которой X и S являются шестнадцатеричными цифрами

Таким образом, команда может выглядеть как li ка один из следующих действий:

  • "s"
  • "srlff" = "s" + "rlff"
  • "rlffwlbb2e2e2e2erlbb" = "s" + "rlff" + "wlbb2e2e2e2e" + "rlbb"
+0

Итак, сколько байтов вы хотите прочитать перед вызовом handleInput? Если вы хотите не блокировать IO, для этой цели существуют классы в пакете nio. – anonymous

+0

, пока не будет прочитано больше байтов. –

+0

Не осталось больше байтов, пока поток не будет закрыт или что-нибудь еще доступно? Если это последний, вы можете прочитать в массиве байтов и преобразовать этот массив байтов в строку HEX, а затем вызвать handleInput(). – anonymous

ответ

1

Вы можете прочитать в массив байтов, как этот

int bytesRead = 0; 
byte[] buffer = new byte[1024]; // reads up to 1024 byte chunks 
while((bytesRead = in.read(buffer)) != -1) { 
    for (int i = 0; i < bytesRead; i++) { 
     input += Integer.toHexString(buffer[i]); 
    } 

    handleInput(input); 
} 

выше кода вызывает то же, что и ваш старый код, который «ввод» продолжает расти и использоваться снова и снова, чтобы вызвать handleInput(). Не уверен, что это ваше намерение, но оно выглядит подозрительно.

+0

@hanno binder Спасибо за редактирование. Возможно ли продвигать изменения? Я бы сделал это мгновенно :). – anonymous

1

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

Поскольку ваш пример не имеет разделителя, мне нужно немного взломать, чтобы продемонстрировать удивительность сканера, но он все равно должен работать для точных команд, которые вы указали. Это не будет хорошим выбором, если вы ожидаете, что словарь команд изменится.

Я бы рекомендовал использовать разделители, если это вообще возможно. Это облегчает жизнь.


Я смотрел на Scanner класс, если я вам.

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

Вот краткий, автономный пример:

package com.stackoverflow.q22199860; 

import java.io.ByteArrayInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.nio.charset.Charset; 
import java.nio.charset.StandardCharsets; 
import java.util.Scanner; 
import java.util.regex.Pattern; 

public class ReadStream 
{ 
    public static void main(String[] args) 
    { 
     Pattern commandPattern = Pattern.compile("s|rl|wl"); 
     String commands = "srlffwlbb2e2e2e2erlbb"; 
     Charset utf8 = StandardCharsets.UTF_8; 

     try (
      InputStream inputStream = new ByteArrayInputStream(commands.getBytes(utf8)); 
      Scanner scanner = new Scanner(inputStream, utf8.name()); 
     ) { 
      scanner.useDelimiter(commandPattern); 
      while(scanner.hasNext()) { 
       String command = scanner.next(); 
       if (command.isEmpty()){ 
        //s 
        System.out.println("s" + command); 
       } else if (command.length() == 2) { 
        //rl 
        System.out.println("rl" + command); 
       } else if (command.length() == 10) { 
        //wl 
        System.out.println("wl" + command); 
       } 
      } 
     } 
     catch (IOException e) 
     { 
      System.err.println("Error Reading Stream"); 
     } 
    } 
} 

Выход из этого:

s 
rlff 
wlbb2e2e2e2e 
rlbb 
+0

Хмм, но для этого потребуется, чтобы команды были разделены .., которые тоже не являются ... команды могут входить как '' command1 "или' "command1command2command3" затем отфильтруйте их с помощью регулярного выражения. –

+1

Как вы знаете, когда вы читали команду, если нет разделителя? Вы просто читаете символы, пока не найдете известную команду? Пример ввода и ожидаемый результат будут полезны. –

+0

@jon quarfoth. Например, ввод и ожидаемый результат. – anonymous

2

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

Что-то вроде:

public void processStream(InputStream in) { 
    List<Command> commands = new ArrayList<Command>(); 
    while((int c = in.getChar()) != -1) { 
      switch((char)c) { 
       case 's': 
        commands.add(new SelectCommand()); 
        break; 
       case 'r': 
        commands.add(ReadCommand.buildFromStream(in)); 
        break; 
       case 'w': 
        commands.add(WriteCommand.buildFromStream(in)); 
        break; 
       case ';': 
        commandEngine.execute(commands); 
        break; 
       default: 
        throw new StreamParseError("unexpected character: " + c); 
      } 
    } 
} 

Это предполагает, что SelectCommand, ReadCommand, WriteCommand являются совместимыми по типу с Command.

... с, например ReadCommand.buildFromStream существа:

public static ReadCommand buildFromStream(InputStream in) { 
     if((char)in.read() != 'n') { 
      throw new StreamParseError("Expect 'l' after 'r'"); 
     } 

     // bad error checking here - be less lazy in real life. 
     String hexNum = in.read() + in.read(); 
     int num = Integer.parseInt(hexNum,16); 
     return new ReadCommand(num); 
    } 

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

Вы также можете использовать Scanner. Чаще всего сканер используется с разделителями, но он также может искать шаблоны регулярных выражений.

Scanner scanner = new Scanner(stream); 
    String cmd = ""; 
    while(cmd != "e") { // I made up an "end" command :) 
     cmd = scanner.findWithinHorizon("(s|rl..|wl.{8}|e)",12); 
     if(cmd == null) { 
      // end of input, or badly formed input 
      break; 
     } 
     handleCmd(cmd); 
    } 
1

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

Это снова приводит к другой проблеме: у вас нет гарантии, что данные вашего потока разделены на «куски» на транспортном уровне. Вы можете получить одну команду плюс половину команды в одном вызове read(buffer), а затем вторую половину команды плюс еще несколько данных в следующем read(buffer).

Поэтому я рекомендую, чтобы вы продолжали читать данные только до тех пор, пока не обнаружите конец одного сообщения/команды/независимо, а затем выполните обработку только для этого единственного сообщения, прежде чем читать больше поступающих данных и повторять. Все остальное (т. Е. Работа с частично полученными сообщениями) легко становится беспорядочным.

+0

То точно, чего я пытаюсь достичь. В основном я надеюсь, что это 'read()', который возвращает либо прочитанный байт, либо -1 на EOS или -2, ни на что не прочитанное в Stream .. –

+0

[InputStream.available()] (http: // docs .oracle.com/javase/6/docs/api/java/io/InputStream.html # доступно% 28% 29) может достичь этого, но я сомневаюсь, что это действительно то, что вы хотите. Посмотрите на настройку [timeout] сокета (http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html#setSoTimeout%28int%29), цитата: «С этим параметр, установленный на ненулевой тайм-аут, вызов read() в InputStream, связанный с этим Socket, будет блокироваться только на этот промежуток времени. Если истечение тайм-аута истекает, возникает исключение java.net.SocketTimeoutException, хотя Socket все еще действителен «. – JimmyB

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