Java InputStream 读取方法为 NFS 安装位置中的文件返回 ASCII 'NUL' 字符

标签 java file io nfs tail

我有一个 Java 进程,它使用 Java RandomAccessFile 读取给定文件,并根据文件内容进行一些处理。该文件是一个日志文件,由另一个 Java 进程更新。读取文件的 java 进程在另一台机器上,并具有 NFS 挂载设置以访问远程服务器中的文件。基本上,读取文件的进程将根据 RandomAccessFile 的文件长度和位置轮询文件中的更改,并为遇到的每个字节调用处理程序方法。问题是我有时会从 RandomAccessFile 读取方法返回 ASCII“NUL”字符

int charInt = read();

也就是说,charInt 在某些情况下返回 0,并在一段时间后返回有效字符。但是后来我在流读取 NULs 期间丢失了字符

我尝试使用 http://commons.apache.org/io/apidocs/org/apache/commons/io/input/Tailer.html我在哪里收到每一行的通知。但是在这些行中,我有时会注意到 ASCII NUL 字符。 我也在 Java IO implementation of unix/linux "tail -f" 中进行了追踪 - 我的 java 进程是类似的,但后来我开始认为问题出在 NFS 挂载或尝试从 NFS 挂载读取时一些错误的 java IO。我从一个普通文件(不在 NFS 装载中)进行了一些测试读取,并有一个连续写入它的进程。所有这些测试都成功了。 我还尝试了 java BufferedReader,因为文件流实际上是一个字符流,尽管我可以将其视为字节流。我仍然得到 NUL 字符。

不确定这是否重要 - NFS 挂载是只读 (ro) 挂载。 感谢对此的任何帮助。谢谢。

我也尝试了以下方法:

FileWriter fileWriter;
    try {
        fileWriter = new FileWriter("<OUT_FILE>", true);
    } catch (IOException e) {
        throw new RuntimeException("Exception while creating file to write sent messages ", e);
    }
    BufferedWriter bufWriter = new BufferedWriter(fileWriter);

    Runtime r = Runtime.getRuntime();
    Process p = r.exec("tail -f <PATH_TO_IN_FILE>");
    Scanner s = new Scanner(p.getInputStream());
    while (s.hasNextLine()) {     
        String line = s.nextLine(); 
        bufWriter.write(line);
        bufWriter.write(System.getProperty("line.separator"));
        bufWriter.flush();

    }
    bufWriter.close();                               

但我仍然得到 NUL 字符。在这里,我将读取的行写入文件,以便我可以比较 IN 文件和 OUT 文件。我看到有一次跳过了行(带有 NUL 字符)。所有其他行都比较好 - 所以从大约 13000 行中,我们看到大约 100 行不匹配。另外一件奇怪的事情是我跑得少了,我也可以在这里看到 NUL 字符,基本上是 ^C^@^@^@^@^@^@^@^@^@ 的形式^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@ 然后是有效行。在错过这些行的时候我注意到的另一件事是,文件在写入过程中更新得非常快,所以基本上一条 xml 消息在 20110729 13:44:06.070097 写入文件,然后在 20110729 13 写入下一条消息:44:06.100007。第二个 xml 消息中缺少行。更多发现:我们正在读取文件的文件路径位于共享 NAS 中。

最佳答案

我意识到这个问题现在已有一年多了,但我会添加我所知道的,以防其他有这个问题的人像我一样偶然发现它。

这个问题中描述的 NUL 字符是由于异步写入正在读取的文件而出现的。更具体地说,来自远程文件写入器的数据包乱序到达,NAS 缓冲区提交了一个稍后的数据包,并用 NUL 字符填充未接收数据的区域。当收到丢失的数据包时,NAS 缓冲区会提交它,覆盖那些空字符。

在我们第一次遇到这种情况的应用程序中,我们正在逐行读取文件,并跟踪成功读取的最后一个行号(因此我们可以随时停止并从我们停止的地方重新开始)。我们处理这个问题的临时解决方案是在每次读取时专门检查“\0”,当遇到它时,关闭文件,等待 1 秒并重新打开文件,排队到我们停止的地方。通常,当我们再次读取该行时,实际文本已经提交。

虽然关闭并重新打开文件看起来很戏剧化,但不这样做就无法恢复。您不能标记/重置 BufferedReader 来解决它,因为一旦字符被读入读取器的缓冲区,它们将不会从文件中重新读取,只会在您每次尝试再次读取时反省。

获取底层 FileChannel 以及读取和设置 position() 也会失败,因为您在文件中的位置包括您可能尚未看到的读入缓冲区的字符,并且您最终将跳过那些看不见的数据。

我们正在测试一个解决方案,我们扩展了 InputStreamReader 类并覆盖了 read(char[], int, int) 方法以使用文件 channel 在每次读取之前获取位置,调用父类(super class)的 read 方法,检查\0 并在找到时重置文件 channel 位置,返回 0 作为读取的字符数。

关于Java InputStream 读取方法为 NFS 安装位置中的文件返回 ASCII 'NUL' 字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6814404/

相关文章:

java - 如何在使用 GraphStream(或其他库)扩展 JPanel 的自定义 java swing 组件上绘制节点和边?

php - 填满的 $_FILES 不返回文件扩展名

file - 从字节偏移量开始读取文件的一行,直到新行

C++ - 在我自己的类中使用 ostream

java - Weblogic 类加载器多次加载类

java - 如何将条码扫描仪集成到 Java 库存管理程序中

c++ - 监视音频文件以检查它何时被另一个程序打开

linux - 为什么在 Linux 中做 I/O 是不间断的?

ruby - 如何在 Ruby 中逐行读取数据?

java - 从用户获取日期(mm/dd/yyyy),检查是否有效,打印日期(月,日,年)