技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

java:標準入出力の入力元・出力先を変更する

javaには
・System.setIn
・System.setOut
・System.setErr
の3つのメソッドが用意されており、これらのメソッドにより標準入出力の入力元・出力先を変更することができます。
これらのメソッドを利用することで、標準入力や標準出力を使用している処理の立ち振る舞いを外から変えることができます。
 
例えば、JUnitでテストする時に便利だと思います。
 
以下は、標準入力をハードコーディングで与え、標準出力・標準エラー出力をファイルに出力する例です。
 
【サンプルコード】
・SystemInOut.java
import java.util.Scanner;

public class SystemInOut {
  public void execute() throws InterruptedException {
    // try-with-resources
    try (Scanner sc = new Scanner(System.in)) {
      String str = sc.nextLine();
      System.out.println("入力された文字:" + str);
      Thread.sleep(500); // 標準出力してから標準エラー出力する
      System.err.println("エラー無し");
    }
  }
}
 
・SystemInOutMain.java
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class SystemInOutMain {
  public static void main(String[ ] args)
      throws InterruptedException, IOException {

    // 標準入出力のオブジェクトを退避
    InputStream orgIn = System.in;
    PrintStream orgOut = System.out;
    PrintStream orgErr = System.err;

    // 標準入力の入力元を変更
    class MyInputStream extends InputStream {
      private StringBuilder sb = new StringBuilder();

      // 標準入力したい文字を登録。nextLineに対応するため改行を付与。
      public void typeLine(String str){
        sb.append(str).append(System.getProperty("line.separator"));
      }

      // InputStreamの仕様上readメソッドの実装の必要あり
      @Override
      public int read() {
        if (sb.length() == 0) {
          return -1; // ストリームの終わり
        }
        int result = sb.charAt(0); // 次に読み込むバイト値
        sb.deleteCharAt(0); // 読み込んだので削除
        return result;
      }
    }
    MyInputStream myIn = new MyInputStream();
    System.setIn(myIn); // 自作の標準入力オブジェクトをセット
    myIn.typeLine("hoge"); // 標準入力に相当する操作

    // 標準出力の出力先を変更
    ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
    PrintStream myOut = new PrintStream(baos1);
    System.setOut(myOut);

    // 標準エラー出力の出力先を変更
    ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
    PrintStream myErr = new PrintStream(baos2);
    System.setErr(myErr);

    // 標準入出力を含む処理を実行
    SystemInOut systemInOut = new SystemInOut();
    systemInOut.execute();

    // 標準出力相当の出力をStringで受け取りファイル出力
    String outStr = new String(baos1.toByteArray());
    FileWriter fw1 = new FileWriter("C:\\tmp\\test1.txt");
    fw1.write(outStr);
    fw1.close();

    // 標準エラー出力相当の出力をStringで受け取りファイル出力
    String errStr = new String(baos2.toByteArray());
    FileWriter fw2 = new FileWriter("C:\\tmp\\test2.txt");
    fw2.write(errStr);
    fw2.close();

    // 後処理
    myIn.close();
    myOut.close();
    baos1.close();
    myErr.close();
    baos2.close();
    System.setIn(orgIn);
    System.setOut(orgOut);
    System.setErr(orgErr);

    // 後処理により標準入出力の入力元・出力先が戻ることも確認済
    // systemInOut.execute();
  }
}
 
【処理結果】
・コンソールは入出力無し
 
・C:\tmp\test1.txt
入力された文字:hoge
 
・C:\tmp\test2.txt
エラー無し