当前位置: 首页 > 知识库问答 >
问题:

控制台I/O重定向后清空InputStream

郎欣然
2023-03-14

我必须测试一个程序,用户必须输入3个数字,然后他在控制台输出中得到结果。

我编写了测试并重定向了系统。在和系统中。出来我的测试用例如下所示:

private static ByteArrayOutputStream mockOut;

@Test
public void correct_input_test() {
    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

这是作为第一个参数的预期控制台输出和准备输入流的3 int参数。

test()方法如下:

private static void test(String expected, int ... args) {
    set(args);       // preparing inputstream and redirecting Sys.i/o
    run(expected);   // running program test after setting up
}

根据需要允许尽可能多的整数输入。这些参数进一步传递给set()方法,在该方法中,我创建了一个Input流并重定向System.inset()方法:

private static void set(int... args) {
    String s = Arrays.stream(args)
               .mapToObj(String::valueOf)
               .collect(Collectors.joining(" "));          // assembling args String
    System.setIn(new ByteArrayInputStream(s.getBytes()));  // redirecting Sys.in 
    mockOut = new ByteArrayOutputStream();
    PrintStream stream = new PrintStream(mockOut);
    System.setOut(stream);                                 // redirecting Sys.out
}

传递的参数1、0、0变成字符串s=“1 0 0”

最后,在设置之后,我运行调用我的程序的方法

private static void run(String expected) {
    Main.main(null);                                              // call program to catch result
    String[] consoleLines = mockOut.toString().split("\n");       // split console output
    assertEquals(expected, consoleLines[consoleLines.length-1]);  // compare lines
}

在这里,我将在mockOut对象中获得的控制台输出分成几行,并将最后一行与预期的字符串进行比较。

只有当我测试一个测试用例(如果我注释掉其中任何一个测试用例)时,它才有效。

@Test
public void correct_input_test() {
//    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

但是,如果我在第二次尝试调用程序之后尝试运行这两个程序,它会得到一个空的java输入流。util。我不知道为什么。我试过关闭溪流,但我想没关系。每次调用set()时,我都会使用非空的参数字符串创建新的InputStream。我还试图恢复默认系统。并在set()中再次重定向-无效。

谁能解释一下我到底忘记了什么?

UPD。我将我的代码与方法分开来解释哪个是哪个,但以防它会增加易读性-下面是完全相同的代码:

private static ByteArrayOutputStream mockOut;

@Test
public void correct_input_test() {
    test("> x = 0, y = 0", 1, 0, 0);
    test("> x = -1, y = 0", 1, 0, -1);
}

private static void test(String expected, int ... args) {
    set(args);       // preparing inputstream and redirecting Sys.i/o
    run(expected);   // running program test after setting up
}

private static void set(int... args) {
    String s = Arrays.stream(args)
               .mapToObj(String::valueOf)
               .collect(Collectors.joining(" "));          // assembling args String
    System.setIn(new ByteArrayInputStream(s.getBytes()));  // redirecting Sys.in 
    mockOut = new ByteArrayOutputStream();
    PrintStream stream = new PrintStream(mockOut);
    System.setOut(stream);                                 // redirecting Sys.out
}

private static void run(String expected) {
    Main.main(null);                                              // call program to catch result
    String[] consoleLines = mockOut.toString().split("\n");       // split console output
    assertEquals(expected, consoleLines[consoleLines.length-1]);  // compare lines
}

UPD。下面是堆栈跟踪:

java.util.NoSuchElementException: 

Arguments input incomplete.

    at Main.in(Main.java:169)
    at Main.main(Main.java:19)
    at MainTest$MainMethodTest.run(MainTest.java:75)
    at MainTest$MainMethodTest.test(MainTest.java:71)
    at MainTest$MainMethodTest$SwapMethodTest.correct_input_test(MainTest.java:29)

我知道异常的确切来源。这是in()方法,它从main()调用以获取用户的输入,并且当Scanner尝试从输入流读取Next()时捕获异常:

public static int in(String message, int from, int to) {
    if (message != null && !message.isEmpty()) {
        System.out.print(message);
    }
    try {
        String input = scanner.next();
        int num;

        /*         this code works correctly for me
                   and is just to check 
                   if user typed correct number 
                   and if the number is in range.
        try {
            if ((num = Integer.parseInt(input)) < from) {
                throw new IllegalArgumentException("\n\nExpected: value >= " + from + "\nActual:   value = " + num + "\n");
            } else if (num > to) {
                throw new IllegalArgumentException("\n\nExpected: value <= " + to + "\nActual:   value = " + num + "\n");
            } else {
                return num;
            }
        } catch (NumberFormatException e) {
            throw new NumberFormatException("\n\nExpected: integer number\nActual:   " + input + "\n");
        }
        */

    } catch (NoSuchElementException e) {
        throw new NoSuchElementException("\n\nArguments input incomplete.");
    }   // at Main.in(Main.java:169) - here is line 169
}

但我不明白为什么是空的。

共有1个答案

翟奇
2023-03-14

好看来我解决了。

我刚刚将Scanner初始化从变量声明移动到main()。

public class Main {
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        // ....
    }
}

现在它对我有效了。

public class Main {
    private static Scanner scanner;

    public static void main(String[] args) {
        scanner = new Scanner(System.in);
        // ....
    }
}
 类似资料:
  • 默认情况下始终有3个"文件"处于打开状态, stdin(键盘),stdout(屏幕), andstderr(错误消息输出到屏幕上). 这3个文件和其他打开的文件都可以被重定向. 对于重定向简单的解释就是捕捉一个文件, 命令, 程序, 脚本, 或者甚至是脚本中的代码块(参见 Example 3-1和 Example 3-2)的输出, 然后将这些输出作为输入发送到另一个文件, 命令, 程序, 或脚本中

  • 所以,我的代码是 但是控制台是空的。没有错误,什么都没有。我做错了什么?

  • 有如 while, until, 和 for 循环, 甚至 if/then 也可以重定向 标准输入 测试代码块. 甚至连一个函数都可以用这个方法进行重定向 (见 样例 24-11). 代码块的末尾部分的 “<” 就是用来完成这个的. 样例 20-5. while 循环的重定向 #!/bin/bash # redir2.sh if [ -z "$1" ] then Filename=names.

  • 限制模式下被禁用的命令 在限制模式下运行一个脚本或部分脚本将禁用一些命令,尽管这些命令在正常模式下是可用的。这是个安全措施,可以限制脚本用户的权限,减少运行脚本可能带来的损害。 被禁用的命令和功能: 使用 cd 来改变工作目录。 修改 $PATH, $SHELL, $BASH_ENV 或 $ENV 等环境变量 读取或修改 $SHELLOPTS,shell环境选项。 输出重定向。 调用包含 / 的命

  • 一个 exec < filename 命令重定向了 标准输入 到一个文件。自此所有 标准输入 都来自该文件而不是默认来源(通常是键盘输入)。在使用 sed 和 awk 时候这种方式可以逐行读文件并逐行解析。 样例 20-1. 使用 exec 重定向 标准输入 #!/bin/bash # 使用 'exec' 重定向 标准输入 . exec 6<&0 # 链接文件描述符 #6 到标准

  • and 列表 和 or 列表 结构提供了连续执行若干命令的方法,可以有效地替换复杂的嵌套 if/then ,甚至 case 语句。 链接多个命令 and 列表 command-1 && command-2 && command-3 && ... command-n 只要前一个命令返回 true(即 0),每一个命令就依次执行。当第一个 false(即 非0)返回时,命令链条即终止(第一个返回 fa