【发布时间】:2017-02-21 12:20:21
【问题描述】:
我的问题是我被指派修改和改进一个执行 LZW 压缩的程序。据我所知,我的程序运行良好,但依赖于 System.in 和 System.out 重定向输入文件和输出文件。对于我的测试程序,我运行了一个充满文件的文件夹,并且对于每个文件,对程序运行 4 个不同的测试。为了模拟命令行输入/输出重定向,在每个文件的循环的第一次迭代中,我执行以下操作:
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
第一次运行我正在测试的程序时,它运行良好并返回一个压缩比并准备将其写入程序输出文件。
但是,在第二次程序调用时(如果我要消除所有额外的调用,则 for 循环的第二次迭代)它崩溃并返回输入流为空。我还没有机会看看输出流是否会做同样的事情,但我试图重置流的方法如下:
在每次调用程序调用之前,我都有以下代码块,我**认为可以解决问题,但无济于事:
//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
我尝试了各种不同方法的组合来重置输入流,但每个解决方案仍然返回相同的结果,一条错误消息表明它正在从空输入流中读取 - 一个错误意味着该流是在文件末尾(或文件中没有任何内容)。
我能得到的唯一其他错误是,如果我尝试使用 mark(0),则在调用 reset 时不支持 mark();和重置();方法。
我已经阅读完毕,但找不到任何可靠的答案来说明如何让它发挥作用。该程序将StdIn 转换为BufferedInputStream 和StdOut 转换为BufferedOutputStream,因此我需要一种兼容并提供类似于StdIn/StdOut 重定向的性能的方法。
TL;DR:我需要重置我的 FileInputStream,但使用 mark() reset() 或重新初始化 FileInputStream 都无法这样做
更新: 尝试按照类似帖子中的建议将 FIS 包装到 BufferedInputStream 中,其中支持 mark()。但是,mark() 和 reset() 方法仅适用于 BufferedInputStream 的大小(我认为是 4096 字节),这几乎排除了我所有文件中标记/重置功能的潜在用途。它们都大于 4kB :(
更新 2: 我决定将完整的 sn-p 代码从我开始 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.out......” 将其更改为依赖 InputStream 和 OutputStream。这样会更加灵活,并且您可以将新流传递给每个调用。
-
@VGR 感谢您的建议,如果一切都失败了,我会尝试这个,但我宁愿不必编辑程序运行的依赖项,因为我按原样获得了依赖项并告诉不要编辑它们。我应该接触的唯一文件是控制压缩的源文件。这更像是一种想弄清楚如何让我的程序以这种方式工作的好奇心,而不是让它以这种方式工作的绝对必要性。简而言之,我主要想弄清楚如何根据未来工作的需要动态更改 StdOut 和 StdIn。非常感谢您的建议:)
-
如果您想重新启动 FileInputStream,只需创建一个新的(或切换到 RandomAccessFile)。流在设计上只能读取一次 - 标记/重置系统只是在没有其他选择或您只需要倒带几个字节的情况下的拐杖。
-
@Robert 感谢您的回复。我尝试同时使用 FileInputStream 和 BufferedInputStream。在一个例子中,我尝试重新初始化已经声明的 FIS/BIS,并且我还尝试声明一个全新的并将 System.in 切换到那个。他们都在程序第二次运行时读取了一个空的输入流,我确实重新初始化了 BIS/FIS(我都试过了)并将 System.in 设置为适当的参考。这让我很摸不着头脑。
标签: java inputstream fileinputstream system.out system.in