Труба между процессами Java на командной оболочке не надежно работает - PullRequest
1 голос
/ 26 апреля 2011

Я пытаюсь передать текст между двумя программами Java. Для простоты я представляю этот код:

import java.io.DataInputStream;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        String completeText = ""; 

        while (stdin.available() > 0) {
            byte[] tempByte = { stdin.readByte() };
            completeText += new String(tempByte);
        }

        System.out.println(completeText);
    }
}

При выполнении следующих действий в Linux или Windows текст, кажется, пропускается, как если бы канал блокировался или терялся совершенно случайно. Иногда все проходит, иногда нет:

echo "omg wtf" | java Test | java Test

Есть идеи по этому поводу? Кажется, чем медленнее процессор, тем чаще проходит текст. Является ли «доступным» возвращающий неверный результат по любой причине, когда входные данные передаются из java System.out.println ()?

ура!

Ответы [ 2 ]

4 голосов
/ 26 апреля 2011

Во-первых, метод available() не является надежным способом определить, исчерпан ли поток.Надежное указание конца потока - проверка возвращаемого значения метода read() (<0 означает конец потока). </p>

Короче говоря, available() может вернуть false(который завершит цикл), если поток на мгновение пуст.Если канал все еще активен, эта ситуация изменится, как только процесс на другом конце канала запишет в него несколько байтов.Чтобы быть уверенным, что все данные прочитаны, вам нужно проверить конец потока.

Во-вторых, если вы хотите прочитать символы (и объединить их в строку), вы должны прочитать символы из Читатель (а не байты из потока).Это позволит вашему коду обрабатывать символы Юникода.

В-третьих, конкатенация больших кусков символов будет быстрее, если вы используете StringBuilder (вместо простой строки).

Наконец, если вам нужно только прочитать байты, вы можете напрямую использовать поток ввода (не нужно оборачивать его DataInputStream).

Вот как я бы написал:

Reader r = new InputStreamReader(System.in);
StringBuilder sb = new StringBuilder();
while(true) {
  int ch = r.read();
  if(ch < 0)
    break;
  sb.append((char) ch);
}

System.out.println(sb.toString());
3 голосов
/ 26 апреля 2011

available() не является надежным для ввода по каналу. Он проверяет, есть ли данные во входном буфере текущего процесса. Он не может проверить, собирается ли предыдущий (конвейерный) процесс отправить некоторые данные.

В вашем случае блокировка чтения является приемлемым решением:

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        StringBuilder completeText = new StringBuilder(); 
        byte[] tempByte = new byte[1024];
        int len = 0;  
        while ((len = stdin.read(tempByte)) != -1) {
            completeText.append(new String(tempByte, 0, len));
        }
        System.out.println(completeText.toString());
    }
}

Я также добавил StringBuilder, так как это «правильный» способ Java для объединения строк.

...