1. 概要

ユーザーの入力を受け入れるJavaアプリケーションを作成する場合、単一行入力と複数行入力の2つのバリエーションがあります。

単一行入力の場合、処理は非常に簡単です。 改行が表示されるまで入力を読み取ります。 ただし、複数行のユーザー入力を別の方法で管理する必要があります。

このチュートリアルでは、Javaで複数行のユーザー入力を処理する方法について説明します。

2. 問題を解決するためのアイデア

Javaでは、 Scanner クラスを使用して、ユーザー入力からデータを読み取ることができます。 したがって、ユーザー入力からデータを読み取ることは、私たちにとって難しいことではありません。 ただし、ユーザーが複数行のデータを入力できるようにする場合は、ユーザーが受け入れる必要のあるすべてのデータをいつ提供したかを知る必要があります。 つまり、ユーザー入力からの読み取りをいつ停止する必要があるかを知るためのイベントが必要です。

一般的に使用されるアプローチはユーザーが送信するデータを確認します。 データが定義された条件に一致する場合、入力データの読み取りを停止します。 実際には、この条件は要件によって異なります。

この問題を解決するためのアイデアは、無限ループを記述して、ユーザー入力を1行ずつ読み取り続けることです。 ループでは、ユーザーが送信する各行をチェックします。 条件が満たされると、無限ループが解除されます。

while (true) {
    String line = ... //get one input line
    if (matchTheCondition(line)) {
        break;
    }
    ... save or use the input data ...
}

次に、アイデアを実装するためのメソッドを作成しましょう。

3. 無限ループを使用して問題を解決する

簡単にするために、このチュートリアルでは、アプリケーションが文字列「bye」(大文字と小文字を区別しない)を受信すると、入力の読み取りを停止します。

したがって、前に説明したアイデアに従って、問題を解決するためのメソッドを作成できます。

public static List<String> readUserInput() {
    List<String> userData = new ArrayList<>();
    System.out.println("Please enter your data below: (send 'bye' to exit) ");
    Scanner input = new Scanner(System.in);
    while (true) {
        String line = input.nextLine();
        if ("bye".equalsIgnoreCase(line)) {
            break;
        }
        userData.add(line);
    }
    return userData;
}

上記のコードが示すように、 readUserInput メソッドは、 System.in からユーザー入力を読み取り、 userDataListにデータを格納します。

ユーザーから「さようなら」を受け取ると、無限のwhileループを中断します。 つまり、ユーザー入力の読み取りを停止し、userDataを返してさらに処理します。

次に、メインメソッドreadUserInputメソッドを呼び出しましょう。

public static void main(String[] args) {
    List<String> userData = readUserInput();
    System.out.printf("User Input Data:\n%s", String.join("\n", userData));
}

main メソッドでわかるように、 readUserInput を呼び出した後、受信したユーザー入力データを出力します。

それでは、アプリケーションを起動して、期待どおりに機能するかどうかを確認しましょう。

アプリケーションが起動すると、次のプロンプトが表示されて入力を待ちます。

Please enter your data below: (send 'bye' to exit)

それでは、テキストを送信して、最後に「さようなら」を送信しましょう。

Hello there,
Today is 19. Mar. 2022.
Have a nice day!
bye

bye」と入力してEnter を押すと、アプリケーションは収集したユーザー入力データを出力して終了します。

User Input Data:
Hello there,
Today is 19. Mar. 2022.
Have a nice day!

これまで見てきたように、この方法は期待どおりに機能します。

4. ソリューションの単体テスト

問題を解決し、手動でテストしました。 ただし、新しい要件に適応するために、メソッドを調整する必要がある場合があります。 したがって、メソッドを自動的にテストできればよいでしょう。

readUserInput メソッドをテストするための単体テストの記述は、通常のテストとは少し異なります。 これは、 readUserInputメソッドが呼び出されると、アプリケーションがブロックされ、ユーザー入力を待機しているためです。

次に、最初にテスト方法を見てから、問題がどのように解決されるかを説明します。

@Test
public void givenDataInSystemIn_whenCallingReadUserInputMethod_thenHaveUserInputData() {
    String[] inputLines = new String[]{
        "The first line.",
        "The second line.",
        "The last line.",
        "bye",
        "anything after 'bye' will be ignored"
    };
    String[] expectedLines = Arrays.copyOf(inputLines, inputLines.length - 2);
    List<String> expected = Arrays.stream(expectedLines).collect(Collectors.toList());

    InputStream stdin = System.in;
    try {
        System.setIn(new ByteArrayInputStream(String.join("\n", inputLines).getBytes()));
        List<String> actual = UserInputHandler.readUserInput();
        assertThat(actual).isEqualTo(expected);
    } finally {
        System.setIn(stdin);
    }
}

それでは、メソッドをすばやく見ていき、どのように機能するかを理解しましょう。

最初に、String配列inputLinesを作成して、ユーザー入力として使用する行を保持しました。 次に、期待されるデータを含む expected Listを初期化しました。

次に、トリッキーな部分があります。 現在のSystem.inオブジェクトをstdin変数にバックアップした後、System.setInメソッドを呼び出してシステム標準入力を再割り当てしました。

この場合、 inputLines配列を使用して、ユーザー入力をシミュレートします。

したがって、配列をInputStream 、この場合は ByteArrayInputStream オブジェクトに変換し、InputStreamオブジェクトをシステム標準入力として再割り当てしました。

次に、ターゲットメソッドを呼び出して、結果が期待どおりかどうかをテストできます。

最後に、元のstdinオブジェクトをシステム標準入力として復元することを忘れないでください。 したがって、 System.setIn(stdin);finally ブロックに配置して、とにかく実行されるようにします。

テストメソッドを実行すると、手動の介入なしで合格します。

5. 結論

この記事では、条件が満たされるまでユーザー入力を読み取るJavaメソッドを作成する方法について説明しました。

2つの重要なテクニックは次のとおりです。

  • 標準のJavaAPIのScannerクラスを使用してユーザー入力を読み取る
  • 無限ループで各入力行をチェックします。 条件が満たされた場合は、ループを解除します

さらに、ソリューションを自動的にテストするためのテストメソッドを作成する方法についても説明しました。

いつものように、このチュートリアルで使用されているソースコードは、GitHubからで入手できます。