在 Java 中重置 FileInputStream 以便我可以 运行 测试程序中另一个程序的多个实例

Resetting FileInputStream in Java so I can run multiple instances of another program in a test program

我的问题是我被分配去修改和改进一个进行 LZW 压缩的程序。据我所知,我的程序 运行 很好,但依赖于 System.in 和 System.out 重定向输入文件和输出文件。对于我的测试程序,我 运行 通过一个充满文件的文件夹,并且对于每个文件,运行 对该程序进行 4 种不同的测试。为了模拟命令行 input/output 重定向,在每个文件的循环的第一次迭代中,我执行以下操作:

FileOutputStream fos = new FileOutputStream(compressOut); // compressOut is compressed file
PrintStream ps = new PrintStream(fos);
System.setOut(ps);
FileInputStream fis = new FileInputStream(file); // file is file to be compressed
System.setIn(fis);
fis.mark(0);// mark not supported so this is actually left over from my test code -- wanted to show I tried to do this

第一次 运行 是我正在测试的程序,它运行得非常好并且 returns 压缩率并准备将其写入程序输出文件。

然而,在第二次程序调用时(并且,如果我要消除所有额外调用,for 循环的第二次迭代)它崩溃并且 returns 输入流为空。我还没有机会查看输出流是否会做同样的事情,但我试图重置流的操作如下:

在每次调用程序调用之前,我有以下代码块,我 ** 认为可以解决问题,但无济于事:

//System.setIn(null); // Tried - didn't work
//System.setOut(null); // Tried - didn't work
fos.close();
ps.close();
fis.close();
fos = new FileOutputStream(compressOut);
ps = new PrintStream(fos);
System.setOut(ps);
fis = new FileInputStream(file);
System.setIn(fis);
//fis.reset(); // Tried - didn't work

我已经尝试了各种不同方法的组合来重置输入流,但每个解决方案仍然returns相同的结果,一条错误消息表明它正在从空输入流中读取 - 错误这意味着流在文件的末尾(或者文件中没有任何内容)。

我遇到的唯一其他错误是,如果我尝试使用 mark(0) 调用 reset,则不支持 mark();和重置();方法。

我读完了,但找不到关于如何让它工作的可靠答案。该程序将 StdIn 转换为 BufferedInputStream 并将 StdOut 转换为 BufferedOutputStream,因此我需要一种兼容并提供类似于 StdIn/StdOut 重定向性能的方法.

TL;DR:我需要重置我的 FileInputStream 并且无法使用 mark() reset() 重置 [=19] =]

更新: 尝试按照类似 post 上的建议将 FIS 包装成 BufferedInputStream,其中支持 mark()。但是,mark()reset() 方法仅适用于 BufferedInputStream 的大小(我认为是 4096 字节),这几乎排除了 mark/reset 的潜在用途我所有文件中的功能。它们都大于 4kB :(

更新 2: 我决定 post 从开始 for 循环到出现空流错误的完整代码片段,以防万一任何人都可以看到我看不到的东西。这是有问题的代码:

public static void main(String[] args) throws IOException, InterruptedException {
    File programOutputPath = new File("-- the compression file path --");
    File folderPath = new File("-- the relative folder path --");
    File compressOut = new File(folderPath + "compressed.lzw");
    File[] allFiles = folderPath.listFiles(); // get an array of all files
    FileWriter fw = new FileWriter(programOutputPath);
    for(File file : allFiles) {
        FileOutputStream fos = new FileOutputStream(compressOut);
        PrintStream ps = new PrintStream(fos);
        System.setOut(ps);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        System.setIn(bis);
        String[] cArgs = new String[2];
        cArgs[0] = "-"; cArgs[1] = "-n";
        String[] dArgs = new String[1];
        dArgs[0] = "+";
        String[] LZWArgs = new String[1];
        LZWArgs[0] = "-";
        System.err.println("File: " + file.toString());
        if(!file.canWrite()) {
            System.err.println("SKIPPED FILE");
        }
        if(file.getName().equalsIgnoreCase(".gitignore") || file.getName().equalsIgnoreCase("compressed.lzw")) continue;
        MyLZW.main(cArgs); // runs fine
        if(decompress) {
            //MyLZW.main(dArgs); // not executing this
        }
        long sizeUnc = file.length();
        long sizeC = compressOut.length();
        double ratio = (double)sizeUnc/(double)sizeC; // compression ratio is correct
        System.err.println("java MyLZW - -r <" + file.getName() + "> " + " compressed.lzw compression ratio:" + ratio ); // works fine
        fw.write("java MyLZW - -n <" + file.getName() + "> " + " compressed.lzw compression ratio:" + ratio + "\n");
        cArgs[1] = "-r";
        bis.close(); // close BufferedInputStream
        bis = new BufferedInputStream(new FileInputStream(file)); // reinitialize BIS
        System.setIn(bis); // setIn to newly initialized BufferedInputStream
        MyLZW.main(cArgs); // crashes here b/c empty input stream

听起来问题出在程序内部;它可能会在后续调用中保留对 System.in 原始值的引用。

另一种可能性是程序正在破坏输入文件(将其替换为空文件)作为副作用。

重复调用System.setIn()没有问题,像你这样新建一个FileInputStream确实会从头重新读取文件。因此,问题出在您没有发布的代码上。


在程序内部,输入被初始化为一个静态变量,像这样:

private static BufferedInputStream in = new BufferedInputStream(System.in); 

当加载 class 时,初始化只发生一次。这是一个糟糕的设计,应该被修复。因为 System.in 只读一次,所以您使用 System.setIn() 所做的后续更改将被忽略。

如果您无法正确修复它,您可以通过反射重置该变量,但这个问题可能只是糟糕设计的冰山一角。或者,您可以尝试将此程序隔离在它自己的 class 加载程序中,并在每次主进程运行该程序时创建一个新的 class 加载程序。