Почему мы не можем читать один символ за раз из System.in?

Программа ниже печатает каждый символ, записанный на стандартном, но только после того, как была написана новая строка (по крайней мере, в моей системе!).

public class Test {
    public static void main(String[] args) throws java.io.IOException {
        int c;
        while ((c = System.in.read()) != -1)
            System.out.print((char) c);
    }
}

Это позволяет людям писать такие вещи, как "Нажать любую клавишу, чтобы продолжить", и заставляет что-то вроде "Нажмите enter для продолжения".

  • В чем причина этого?
  • Является ли это ограничением Java?
  • Является ли это поведение зависящим от системы (я на Ubuntu)? Как это работает на Mac? Windows?
  • Зависит ли он от конкретного терминала, в котором я запускаю приложение? (Для меня это ведет себя как в Eclipse и в gnome-терминале)
  • Есть ли обходной путь?

Ответ 1

В чем причина этого?

Большинство терминалов по умолчанию буферизируется по строке, Java не получает ввод до новой строки.

Является ли это ограничением Java?

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

Является ли это поведение зависящим от системы (я на Ubuntu)? Как это работает на Mac? Windows?

Да.

Зависит ли он от конкретного терминала, в котором я запускаю приложение? (Для меня это ведет себя как в Eclipse и в гном-терминале)

Да.

Есть ли способ обхода?

Существуют специфические хаки платформы. curse в Linux и Unix-подобных платформах и getch() в Windows. Я не знаю о кросс-платформенном способе.

связанный: почему "нажмите любую клавишу для продолжения" - это плохая идея:

alt text

Ответ 2

Я на Ubuntu

Есть ли способ обхода?

Runtime.getRuntime().exec("stty -icanon min 1").waitFor();

И после этого все прочтения System.in в том же процессе будут читать 1 символ, не ожидающий EOL.

Ответ 3

см. мой ответ в Эквивалентная функция для C '_getch() " в Java?


public static void getCh() {

    final JFrame frame = new JFrame();

    synchronized (frame) {

        frame.setUndecorated(true);

        frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);

        frame.addKeyListener(new KeyListener() {

            public void keyPressed(KeyEvent e) {

                synchronized (frame) {

                    frame.setVisible(false);

                    frame.dispose();

                    frame.notify();

                }

            }  



        public void keyReleased(KeyEvent e) {  
        }  

        public void keyTyped(KeyEvent e) {  
        }  
    });  
    frame.setVisible(true);  
    try {  
        frame.wait();  
    } catch (InterruptedException e1) {  
    }  
}  

}