จะอ่านอักขระเดี่ยวจากคอนโซลใน Java ได้อย่างไร (ตามที่ผู้ใช้พิมพ์)


110

มีวิธีง่ายๆในการอ่านอักขระเดี่ยวจากคอนโซลขณะที่ผู้ใช้พิมพ์ใน Java หรือไม่? เป็นไปได้ไหม? ฉันได้ลองใช้วิธีการเหล่านี้แล้ว แต่ทุกคนรอให้ผู้ใช้กดปุ่มEnter :

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

ฉันเริ่มคิดว่า System.in ไม่ทราบถึงอินพุตของผู้ใช้จนกว่าจะกดEnter

คำตอบ:


57

สิ่งที่คุณต้องการทำคือทำให้คอนโซลอยู่ในโหมด "ดิบ" (การแก้ไขบรรทัดโดยข้ามและไม่ต้องใช้คีย์ Enter) ซึ่งตรงข้ามกับโหมด "สุก" (การแก้ไขบรรทัดด้วยคีย์ Enter) ในระบบ UNIX คำสั่ง 'stty' สามารถ เปลี่ยนโหมด

ตอนนี้ด้วยความเคารพ Java ... ดูไม่ใช่การปิดกั้นการป้อนข้อมูลคอนโซลในหลามและ Java ข้อความที่ตัดตอนมา:

หากโปรแกรมของคุณต้องใช้คอนโซลคุณต้องเปลี่ยนเทอร์มินัลของคุณออกจากโหมดบรรทัดเป็นโหมดอักขระและอย่าลืมกู้คืนก่อนที่โปรแกรมของคุณจะหยุดทำงาน ไม่มีวิธีพกพาในการดำเนินการนี้ในระบบปฏิบัติการ

คำแนะนำอย่างหนึ่งคือการใช้ JNI อีกครั้งที่ไม่ค่อยพกพา ข้อเสนอแนะในตอนท้ายของด้ายและเหมือนกันกับการโพสต์ดังกล่าวข้างต้นคือการดูที่ใช้jCurses


4
JCurses ก็ไม่ค่อยพกพาได้เช่นกัน .... จาก JCurses README: "JCurses ประกอบด้วยสองส่วนคือส่วนอิสระของแพลตฟอร์มและส่วนที่ขึ้นกับแพลตฟอร์มซึ่งประกอบด้วยไลบรารีที่ใช้ร่วมกันซึ่งทำให้การดำเนินการอินพุตและเอาท์พุตดั้งเดิมพร้อมใช้งานสำหรับส่วนแรก .”
Ryan Fernandes

6
@RyanFernandes ฟังดูค่อนข้างพกพาสำหรับฉัน - เครื่องมือเดียวที่สามารถทำงานบนหลายระบบ (โดยใช้การอ้างอิงที่แตกต่างกัน)
Antoniossss

25

คุณต้องเคาะคอนโซลของคุณเข้าสู่โหมดดิบ ไม่มีวิธีการเดินทางที่ไม่ขึ้นอยู่กับแพลตฟอร์มในตัว jCursesอาจจะน่าสนใจ

ในระบบ Unix สิ่งนี้อาจใช้ได้:

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

ตัวอย่างเช่นหากคุณต้องการคำนึงถึงเวลาระหว่างการกดแป้นพิมพ์นี่คือรหัสตัวอย่างเพื่อไปที่นั่น


ทำงานได้ดีสำหรับฉันภายใต้ Linux
MrSmith42

4
ทำงานบน Mac เช่นกัน คุณอาจต้องการพูดถึงว่าstty cooked </dev/ttyควรรันเมื่อโปรแกรมต้องการเปลี่ยนกลับเป็นโหมดบัฟเฟอร์และแน่นอนก่อนที่โปรแกรมจะออก
Kelvin

15

ไม่มีวิธีแบบพกพาในการอ่านอักขระดิบจากคอนโซล Java

วิธีแก้ปัญหาที่ขึ้นกับแพลตฟอร์มบางประการได้ถูกนำเสนอไว้ข้างต้น แต่เพื่อให้พกพาได้จริงคุณต้องละทิ้งโหมดคอนโซลและใช้โหมดหน้าต่างเช่น AWT หรือ Swing


22
ฉันไม่ค่อยเข้าใจว่าทำไมเช่น Mono (หรือ CLR) ถึงใช้System.Console.ReadKeyงานได้กับทุกแพลตฟอร์ม Java ยังแจกจ่าย JVM และ JRE สำหรับทุกแพลตฟอร์มที่มีไลบรารีและการนำไปใช้งานตามแพลตฟอร์มดังนั้นจึงไม่มีข้อแก้ตัว
Martin Macak

13

ฉันได้เขียนRawConsoleInputคลาส Java ที่ใช้JNAเพื่อเรียกใช้ฟังก์ชันระบบปฏิบัติการของ Windows และ Unix / Linux

  • บน Windows จะใช้_kbhit()และ_getwch()จาก msvcrt.dll
  • ใน Unix จะใช้tcsetattr()เพื่อเปลี่ยนคอนโซลเป็นโหมดที่ไม่ใช่ Canonical System.in.available()เพื่อตรวจสอบว่ามีข้อมูลหรือไม่และSystem.in.read()อ่านไบต์จากคอนโซลหรือไม่ A CharsetDecoderใช้ในการแปลงไบต์เป็นอักขระ

สนับสนุนอินพุตที่ไม่ปิดกั้นและการผสมโหมดดิบและอินพุตโหมดบรรทัดปกติ


สิ่งนี้ได้รับการทดสอบ / ทดสอบความเครียดอย่างหนักแค่ไหน?
คดีของ Fund Monica

2
@QPaysTaxes การทดสอบความเครียดเป็นเรื่องยากสำหรับอินพุตคอนโซล ฉันคิดว่าในกรณีนี้การทดสอบในสภาพแวดล้อมต่างๆจะสำคัญกว่า (Windows / Linux เวอร์ชันต่างๆ, 64/32 บิต, Linux ผ่าน SSH, Telnet, พอร์ตอนุกรมหรือคอนโซลเดสก์ท็อปเป็นต้น) จนถึงตอนนี้ฉันใช้มันในเครื่องมือทดสอบส่วนตัวเท่านั้น แต่ซอร์สโค้ดมีขนาดค่อนข้างเล็กเมื่อเทียบกับโซลูชันอื่น ๆ (เช่น JLine2 ซึ่งใช้ Jansi) จึงมีไม่มากที่จะผิดพลาด ฉันเขียนมันเนื่องจาก JLine2 ไม่รองรับการป้อนอักขระเดี่ยวโดยไม่มีการบล็อก
Christian d'Heureuse

นั่นคือสิ่งที่ฉันหมายถึงจากการทดสอบความเครียด - อาจเป็นคำที่ผิด ความผิดฉันเอง. ยังไงก็ดี! ฉันขโมยไปแล้ว ^ H ^ H ^ H ^ H ^ H ^ ใช้มันในโครงการของฉันเพื่อโรงเรียนและช่วยได้มาก
คดีของ Fund Monica

เฮ้ - คลาสนี้ดูดีมาก อย่างไรก็ตาม: ฉันไม่สามารถใช้งานได้ .. ฉันควรจะใช้มันอย่างไร? ฉันพบ System.in ปิดกั้นจนกระทั่งฉันกด CTRL + D (บน Linux) และตอนนี้ฉันอ่านเกี่ยวกับโหมดคอนโซลและสิ่งที่ชอบ ฉันคิดว่า RawConsoleInput ของคุณคือสิ่งที่ฉันกำลังมองหา - แต่ฉันจะใช้มันได้อย่างไร?
Igor

@Igor เพียงเรียก RawConsoleInput.read (บูลีน) เพื่ออ่านอักขระแป้นพิมพ์ มีการบันทึกไว้ในซอร์สโค้ด (RawConsoleInput.java)
Christian d'Heureuse

8

ใช้jline3 :

ตัวอย่าง:

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();

ฉันพบว่าโซลูชันที่ใช้ RawConsoleInput ไม่ทำงานบน MacOS High Sierra อย่างไรก็ตามมันทำงานได้อย่างสมบูรณ์แบบ
RawToast

jline มีทุกสิ่งที่คุณต้องการในการสร้างระบบคอนโซล / เทอร์มินัลแบบโต้ตอบ ใช้งานได้ดีใน Linux เพื่อให้ดูตัวอย่างที่สมบูรณ์เพิ่มเติมได้ที่: github.com/jline/jline3/blob/master/builtins/src/test/java/org/... มีการเติมข้อความอัตโนมัติประวัติมาสก์รหัสผ่าน ฯลฯ
lepe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.