2

我很难调试我一直在构建的应用程序的问题。我似乎无法用具有相同问题的代表性测试程序来重现问题本身,这使得难以证明。不幸的是,由于安全原因,我无法分享我的实际来源,但是,以下测试很好地代表了我正在做的事情,文件和数据是 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 文件之间的某个位置。

4

1 回答 1

2

终于找到原因了。设法重现该问题几次,并将原因追溯到输入端而不是代码的输出端。我的文件读取功能本质上是这样的:

char[] buf;
int charcount = 0;
StringBuilder line = new StringBuilder(2048);
InputStreamReader reader = new InputStreamReader(stream);// provides a line-wise read
BufferedReader file = new BufferedReader(reader);
do { // capture loop
    try {
    buf = new char[2048];
    charcount = file.read(buf, 0, 2048);
    } catch (IOException e) {
    return null; // unknown IO error
    }
    line.append(buf);
} while (charcount != -1);
// close and output

问题是附加了一个未满的缓冲区,所以后面的值仍然是它们的初始值 null。我无法重现它的原因是因为有些数据很好地填充了缓冲区,有些则没有。

为什么我似乎无法在我仍然不知道的文本编辑器上查看问题,但我现在应该能够解决这个问题。欢迎任何关于最佳方式的建议,因为这是我的长期实用程序库之一,我希望它尽可能保持通用和优化。

于 2011-03-24T09:52:55.287 回答