我很难调试我一直在构建的应用程序的问题。我似乎无法用具有相同问题的代表性测试程序来重现问题本身,这使得难以证明。不幸的是,由于安全原因,我无法分享我的实际来源,但是,以下测试很好地代表了我正在做的事情,文件和数据是 unix 样式 EOL,使用 PrintWriter 写入 zip 文件以及使用 StringBuilders 的事实:
public class Tester {
public static void main(String[] args) {
// variables
File target = new File("TESTSAVE.zip");
PrintWriter printout1;
ZipOutputStream zipStream;
ZipEntry ent1;
StringBuilder testtext1 = new StringBuilder();
StringBuilder replacetext = new StringBuilder();
// ensure file replace
if (target.exists()) {
target.delete();
}
try {
// open the streams
zipStream = new ZipOutputStream(new FileOutputStream(target, true));
printout1 = new PrintWriter(zipStream);
ent1 = new ZipEntry("testfile.txt");
zipStream.putNextEntry(ent1);
// construct the data
for (int i = 0; i < 30; i++) {
testtext1.append("Testing 1 2 3 Many! \n");
}
replacetext.append("Testing 4 5 6 LOTS! \n");
replacetext.append("Testing 4 5 6 LOTS! \n");
// the replace operation
testtext1.replace(21, 42, replacetext.toString());
// write it
printout1 = new PrintWriter(zipStream);
printout1.println(testtext1);
// save it
printout1.flush();
zipStream.closeEntry();
printout1.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
问题的核心是我在我身边看到的文件正在生成一个 16.3k 字符的文件。我的朋友,无论他是在他的电脑上使用该应用程序,还是他查看与我完全相同的文件,都会看到一个包含 19.999k 个字符的文件,额外的字符是 CRLF,后跟大量的空字符。无论我使用什么应用程序、编码或视图,我根本看不到这些 nul 字符,我只在最后一行看到一个 LF,但我确实看到了一个 20k 的文件。在所有情况下,即使两台机器都是 Windows 机器并且都使用相同的编辑软件进行查看,在两台机器上看到的完全相同的文件也会有所不同。
我还不能用任何数量的虚拟程序重现这种行为。但是,我已经能够将最后一行的杂散 CRLF 追溯到我在 PrintWriter 上使用 println。当我用问题替换println(s)
时print(s + '\n')
,问题似乎消失了(文件大小为 16.3k)。但是,当我将程序返回到 println(s) 时,问题似乎没有返回。我目前正在法国的一位朋友验证文件,看看问题是否真的消失了(因为我看不到 nuls 但他可以),但这种行为已经彻底混淆了。
我还注意到 StringBuilder 的替换函数声明“如果需要,这个序列将被延长以适应指定的字符串”。鉴于 stringbuilders setLength 函数使用 nul 字符填充,并且 ensureCapacity 函数将容量设置为较大的输入 or (currentCapacity*2)+2
,我怀疑某处存在关系。但是,在用这个想法进行测试时,我只有一次能够得到一个代表我所看到的结果,并且从那以后就无法重现它。
有谁知道可能导致此错误的原因,或者至少对进行测试的方向提出建议?
编辑,因为评论部分对我来说是坏的:为了澄清,无论操作系统如何,输出都必须是 unix 格式,因此直接使用 '\n' 而不是通过格式化程序。插入的原始 StringBuilder 实际上并不是向我生成的,而是程序读取的文件的内容。我很高兴阅读过程有效,因为其中的信息在整个应用程序中被大量使用。我也做了一些探索,发现在保存之前,缓冲区是正确的容量,并且调用 toString() 时的输出是正确的长度(即它不包含空字符并且长度为 16,363,而不是 19,999 )。这会将错误的原因放在生成字符串和保存 zip 文件之间的某个位置。