JLine 3簡介
一、簡介
JLine是用於處理控制台輸入的函式庫,為我們提供了與 GNU readline庫或ZSH 行編輯器類似的功能。
在本教程中,我們將研究 JLine 3,了解它是什麼、我們可以用它做什麼以及如何使用它。
2. 依賴關係
在使用 JLine 之前,我們需要在建置中包含最新版本,在撰寫本文時為3.28.0 。
如果我們使用 Maven,我們可以在pom.xml
檔案中包含此依賴項:
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.28.0</version>
</dependency>
除此之外,如果我們想在 Windows 上運行,那麼我們還需要 JLine 的終端提供者庫 - 推薦的庫是JANSI :
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jansi</artifactId>
<version>3.28.0</version>
</dependency>
請注意,這在 macOS 和 Linux 系統上不是必需的,因為 JLine 將根據需要在這些作業系統上使用外部函數和記憶體 API (FFM) 或 Java 本機介面 (JNI) 綁定。
此時,我們已準備好開始在應用程式中使用 JLine。
3. 終端
在 JLine 中,我們要使用的中心抽像是Terminal
。這代表終端機或命令列介面,我們可以從中讀取輸入並寫入輸出。我們可以使用TerminalBuilder
來建構它們:
try(Terminal terminal = TerminalBuilder.builder().build()) {
// Use the terminal here.
}
終端實現了Closeable.
這意味著我們可以使用 try-with-resources 模式來確保完成後正確關閉它。
如果我們不自訂建構器,我們將在運行應用程式的系統終端周圍獲得一個包裝器。這樣做會自動從一組支援的類型中偵測終端類型。這使我們能夠使用更高級的輸入和輸出結構。如果未偵測到支援的終端類型,則將回退到對 JLine 功能支援有限的啞終端。
一旦我們獲得了Terminal
實例,我們就可以直接與它互動。 JLine 將輸入和輸出作為InputStream
和OutputStream
實例,或作為Reader
和Writer
實例公開到終端機:
InputStream inputStream = terminal.input();
OutputStream outputStream = terminal.output();
Reader reader = terminal.reader();
PrintWriter writer = terminal.writer();
我們可以完全按照我們的預期使用它們。通常情況下,如果我們要寫入這些輸出流,那麼我們也需要定期刷新它們以確保輸出顯示:
terminal.flush();
JLine 也提供了一些詢問終端的機制 – 查詢諸如大小、遊標位置等資訊:
Size size = terminal.getSize();
請注意,這些僅在終端包裝器支援它們的情況下才有效——後備啞終端可能不支援它們。
4.行讀取器
雖然Terminal
是使用 JLine 的核心,但LineReader
才是使用該函式庫的真正原因。**它與****Terminal**
實例一起接受用戶輸入並支援**複雜的輸入和行編輯**。
在使用LineReader
之前,我們需要建構它。與之前類似,我們使用LineReaderBuilder
來執行此操作:
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.build();
我們至少需要提供我們將要讀取的終端,儘管我們稍後會看到還可以提供許多其他選項。
一旦我們有了LineReader
,我們就可以使用它從終端機讀取輸入:
try {
String line = lineReader.readLine("> ");
terminal.writer().println("Read: " + line);
} catch (Exception e) {
// Handle exception
}
LineReader.readLine()
方法有多個重寫,但最簡單的方法採用String
用作使用者輸入的提示。這將返回用戶輸入的字串供我們在程式中使用:
這也包括編輯我們輸入的字串的能力,既可以簡單地刪除並重新輸入末尾的字符,也可以使用箭頭鍵在字串的中間進行編輯:
如果使用者輸入被中斷,我們的readLine()
方法會拋出例外。如果接收到中斷,則會引發UserInterruptException
(通常由 Ctrl + C 引起) EndOfFileException
處理這些異常。
readLine()
方法的更複雜版本允許我們指定左提示、右提示、初始值和字元遮罩 - 例如,接受密碼輸入:
String line = lineReader.readLine("> ", " <", '#', "Password");
這將使用“>”作為左側提示,使用“<”作為右側提示。它將用“#”符號屏蔽所有輸入字符,並為我們的輸入設定預設值“Password”:
所有參數都是可選的,如果不需要,可以用null
替換。 readLine()
的其他替代方案最終只是對其進行包裝以方便使用。
5. 歷史
除了接受使用者輸入之外,標準LineReader
也會自動為我們提供歷史記錄。這意味著使用箭頭鍵滾動歷史記錄並使用 Ctrl + R 和 Ctrl + S 進行搜尋。這些與 Bash 歷史記錄相同的控制項:
我們使用History
介面的實例來管理歷史記錄,我們在建構時將其與LineReader
關聯起來。預設情況下,這將是DefaultHistory
類別的實例。這會在收到輸入時自動記錄歷史記錄,並允許我們從行讀取器存取它。我們也可以編寫自己的實作並將它們連接到LineReader
:
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.history(new DefaultHistory())
.build();
DefaultHistory
實作允許在LineReader
上進行一些配置,這將透過提供一些選項標誌和一些變數來影響它的工作方式:
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.option(LineReader.Option.HISTORY_IGNORE_DUPS, false)
.variable(LineReader.HISTORY_FILE, Path.of("target/jline-history"))
.variable(LineReader.HISTORY_SIZE, 5)
.build();
此設定允許歷史記錄重複條目,將所有歷史記錄保存到檔案“target/jline-history”以在執行之間共享,並限制調用前五個條目:
6. 完成
除了鍵入我們的輸入之外,JLine 還支援使用Tab
鍵完成命令。我們透過在建構時向LineReader
提供Completer
實例來配置它:
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.completer(completer)
.build();
我們也可以編寫自己的該介面的實作。然而,JLine 附帶了幾個我們可以使用的標準實現。
我們可以使用的最簡單的實作是StringsCompleter
:
Completer completer = new StringsCompleter("foo", "bar", "baz");
這需要一個充當完成候選者的字串清單。當我們按下Tab
鍵時,我們會看到基於此清單的候選者:
如果只有一個完成匹配,則會自動插入它。否則,我們將從多個可能匹配的清單中進行選擇。
我們還有一些其他可以使用的簡單完成器。其中包括EnumCompleter
,它從 Java 枚舉中的條目進行選擇,以及FilesCompleter
、 DirectoriesCompleter
和FileNameCompleter
,它們將根據給定路徑下的檔案和目錄提供完成選項。
如果需要,我們也可以使用AggregateCompleter
組合多個完成器:
Completer completer = new AggregateCompleter(
new StringsCompleter("foo", "bar", "baz"),
new Completers.FilesCompleter(Path.of("/baseDir")),
new Completers.DirectoriesCompleter(Path.of("/baseDir"))
);
現在,這將提供一組固定字串以及給定路徑下的所有檔案和目錄作為我們可能的完成候選:
此外,我們可以使用幾個更複雜的完成器,使我們能夠定義複雜的命令結構:
-
ArgumentCompleter
為指令中的各個單字提供補全候選項.
-
RegexCompleter
將目前指令與正規表示式進行比對,以確定接下來要提供哪些補全。 -
TreeCompleter
允許我們將可能的命令結構建構成樹。
我們能夠將所有這些組合成最適合我們需求的結構。
七、總結
這是對 JLine 的快速介紹。我們可以用這個庫做更多的事情。下次我們需要在基於文字的應用程式中接受使用者輸入時,為什麼不嘗試呢?
與往常一樣,本文中的所有範例都可以在 GitHub 上找到。