Bagaimana cara membaca satu karakter dari konsol di Java (saat pengguna mengetiknya)?

110

Apakah ada cara mudah untuk membaca karakter tunggal dari konsol saat pengguna mengetiknya di Java? Apa itu mungkin? Saya sudah mencoba dengan metode ini tetapi semuanya menunggu pengguna untuk menekan tombol enter :

char tmp = (char) System.in.read();
char tmp = (char) new InputStreamReader(System.in).read ();
char tmp = (char) System.console().reader().read();           // Java 6

Saya mulai berpikir bahwa System.in tidak mengetahui input pengguna sampai enter ditekan.

victor hugo
sumber

Jawaban:

57

Apa yang ingin Anda lakukan adalah menempatkan konsol ke mode "mentah" (pengeditan baris dilewati dan tidak perlu tombol enter) sebagai lawan dari mode "matang" (pengeditan baris dengan tombol enter diperlukan.) Pada sistem UNIX, perintah 'stty' dapat ubah mode.

Sekarang, sehubungan dengan Java ... lihat Input konsol non-pemblokiran di Python dan Java . Kutipan:

Jika program Anda harus berbasis konsol, Anda harus mengalihkan terminal Anda dari mode baris ke mode karakter, dan ingat untuk memulihkannya sebelum program Anda berhenti. Tidak ada cara portabel untuk melakukan ini di seluruh sistem operasi.

Salah satu sarannya adalah dengan menggunakan JNI. Sekali lagi, itu tidak terlalu portabel. Saran lain di akhir utas, dan kesamaan dengan posting di atas, adalah melihat menggunakan jCurses .

Chris W. Rea
sumber
4
JCurses juga tidak terlalu portabel .... Dari JCurses README: "JCurses terdiri dari dua bagian: bagian independen plattform, dan bagian dependen plattform, yang terdiri dari pustaka bersama asli yang membuat operasi input dan output primitif tersedia untuk bagian pertama . "
Ryan Fernandes
6
@RyanFernandes terdengar cukup portabel bagi saya - alat tunggal yang dapat dijalankan di banyak sistem (menggunakan dependensi berbeda)
Antoniossss
25

Anda perlu mengetuk konsol Anda ke mode mentah. Tidak ada cara independen platform bawaan untuk sampai ke sana. jCurses mungkin menarik.

Pada sistem Unix, ini mungkin berhasil:

String[] cmd = {"/bin/sh", "-c", "stty raw </dev/tty"};
Runtime.getRuntime().exec(cmd).waitFor();

Misalnya, jika Anda ingin memperhitungkan waktu di antara penekanan tombol, berikut kode contoh untuk mencapainya.

nes1983
sumber
Bekerja dengan baik untuk saya di Linux
MrSmith42
4
Bekerja di Mac juga. Anda mungkin ingin menyebutkan bahwa stty cooked </dev/ttyharus dijalankan ketika program perlu kembali ke mode buffer, dan pasti sebelum program keluar.
Kelvin
15

Tidak ada cara portabel untuk membaca karakter mentah dari konsol Java.

Beberapa solusi yang bergantung pada platform telah disajikan di atas. Tetapi untuk benar-benar portabel, Anda harus meninggalkan mode konsol dan menggunakan mode windowing, misalnya AWT atau Swing.

rustyx.dll
sumber
22
Saya tidak begitu mengerti mengapa misalnya Mono (atau CLR) memiliki System.Console.ReadKeyyang berfungsi di semua platform. Java juga mendistribusikan JVM dan JRE untuk setiap platform dengan pustaka dan implementasi yang bergantung pada platform, jadi itu bukan alasan.
Martin Macak
13

Saya telah menulis RawConsoleInput kelas Java yang menggunakan JNA untuk memanggil fungsi sistem operasi Windows dan Unix / Linux.

  • Di Windows menggunakan _kbhit()dan _getwch()dari msvcrt.dll.
  • Di Unix yang digunakan tcsetattr()untuk mengalihkan konsol ke mode non-kanonik, System.in.available()untuk memeriksa apakah data tersedia dan System.in.read()untuk membaca byte dari konsol. A CharsetDecoderdigunakan untuk mengubah byte menjadi karakter.

Ini mendukung input non-pemblokiran dan pencampuran mode mentah dan input mode jalur normal.

Christian d'Heureuse
sumber
Seberapa berat hal ini telah diuji / diuji stres?
Dana Gugatan Monica
2
@QPaysTaxes Stress-testing sulit untuk input konsol. Menurut saya, dalam hal ini akan lebih penting untuk mengujinya di berbagai lingkungan (versi Windows / Linux yang berbeda, 64/32 bit, Linux melalui SSH, Telnet, port serial atau konsol desktop, dll.). Sejauh ini saya hanya menggunakannya di alat uji pribadi saya. Tetapi kode sumbernya relatif kecil, dibandingkan dengan solusi lain (seperti JLine2 yang menggunakan Jansi). Jadi tidak banyak yang bisa salah. Saya menulisnya, karena JLine2 tidak mendukung input karakter tunggal tanpa pemblokiran.
Christian d'Heureuse
Itulah yang saya maksud dengan uji stres - mungkin itu kata yang salah; salahku. Pokoknya bagus! Saya telah mencuri ^ H ^ H ^ H ^ H ^ H ^ Huse it dalam proyek saya untuk sekolah dan itu membantu banyak.
Gugatan Dana Monica
Hei - kelas ini tampak hebat. Namun: Saya tidak bisa membuatnya bekerja .. bagaimana saya bisa menggunakannya? Saya telah menemukan pemblokiran System.in sampai saya menekan CTRL + D (di Linux) dan sekarang saya membaca tentang mode konsol dan sejenisnya. Saya pikir RawConsoleInput Anda adalah yang saya cari - tetapi bagaimana cara menggunakannya?
Igor
@Igor Panggil saja RawConsoleInput.read (boolean) untuk membaca karakter keyboard. Ini didokumentasikan dalam kode sumber (RawConsoleInput.java).
Christian d'Heureuse
8

Gunakan jline3 :

Contoh:

Terminal terminal = TerminalBuilder.builder()
    .jna(true)
    .system(true)
    .build();

// raw mode means we get keypresses rather than line buffered input
terminal.enterRawMode();
reader = terminal .reader();
...
int read = reader.read();
....
reader.close();
terminal.close();
Polong
sumber
Saya menemukan bahwa solusi berbasis RawConsoleInput tidak berfungsi di MacOS High Sierra; namun, ini bekerja dengan sempurna.
RawToast
jline praktis memiliki semua yang Anda butuhkan untuk membuat sistem konsol / terminal interaktif. Ini berfungsi dengan baik di Linux. Untuk contoh yang lebih lengkap, lihat: github.com/jline/jline3/blob/master/builtins/src/test/java/org/… . Ini memiliki pelengkapan otomatis, riwayat, topeng kata sandi, dll.
lepe