是否有任何有效的方法来查找文件中特定 4 字节块的所有实例的第一个字节



我有包含存档二进制消息的文件。一个小文件大约 600MB,包含近 9000 条消息。每条消息都以我知道的特定四字节标志开头,该标志指示消息头的前四个字节(因此必须捕获)。邮件头是所有邮件的固定大小。消息标头后跟标头中标识的大小的有效负载。一旦我找到特定消息标头的开头,我就知道标头末尾有多少字节,并可以使用它来提取消息中的字节数,我需要解析此存档文件并隔离每条消息进行处理,确保我包含从四字节标志的第一个字节到指定消息长度末尾的所有字节。消息之间有一些不同的填充。

由于文件的大小,我不想(并且可能不能在所有情况下)将文件用作单个数组。因此,我正在研究诸如RandomAccessFileFileInputStream之类的东西。扫描文件中的特定字节序列,然后从该序列中的第一个字节到已知长度的每个字节似乎不是一项简单的任务。 RandomAccessFile,尤其是read(byte[])seek()方法似乎可以让我实现解决方案。

为了给出一个想法,我当前的实现涉及一种称为findFlag()的方法,该方法在RandomAccessFile中占据起始位置。它寻求该位置并从那里开始读取四个字节。如果找到该标志,则返回startPos .否则,它会递归地调用自己,移动到startPos + 1并重复,直到找到标志。由于我知道作为数据消息的一部分读取的最后一个字节,我将开始在那里寻找:

file.seek(startPos);
byte[] possibleFlag = new byte[4];
file.read(possibleFlag, 0, possibleFlag.length);
if (Arrays.equals(ByteUtils.intToBytes(Message.FLAG), possibleFlag)) {
    return startPos;
}
else {
    return findFlag(startPos + 1);
}

我是否忽略了 Java(Java 6 或更早版本)或经过良好测试的外部库(例如 Apache 库或类似库)中的某些内容?如果没有,是否有更好的解决方案来处理 Java 中的二进制数据或任何特别适合我的问题的方法?

使用

java.nio.channels.FileChannel 扫描文件,它使用较少的中间副本将文件映射到内存中。备选方案基准

这整个方法似乎无效。你怎么知道魔术字节不会出现在其他地方?例如在有效负载或填充中。我希望你考虑到这一点。

摆脱递归。Java不做尾调用消除。迭代版本应该更清晰、更快。

限制分配数量。为每个文件中的每个字节分配两个数组是完全不可接受的。

如果您使用 FileChannel,则不必担心缓冲区大小和分配。您可以使用 MappedByteBuffer.getInt(int) 循环访问该文件,并将其与 Message.FLAG 进行比较。这只是一个简单的 for 循环。

在我看来,

这效率非常低。对文件最昂贵的操作是随机部分 - 来回移动内部指针。你对每一个字节都这样做。+4, -3, +4, -3, 等等...表演死亡华尔兹。你可以完美地做到,只是向前移动。开始只搜索签名的第一个字节,而不是整个序列。如果匹配,请测试下一个字节。如果出现任何失败,只需重新开始搜索第一个字节即可。连续 4 次成功意味着您拥有签名。一直以来,你只是继续前进。不惜一切代价避免寻求。

此外,除非您绝对不在乎处理需要多长时间,否则您不应仅以功能为由关闭 FileChannel。参考的统计数据正在谈论每 100MB 的分钟数,我可以支持这一观察结果。FileChannel 比读取量小的 RandomAccessFile 快两个数量级 - 您需要最小的一个:)

虽然递归通常被认为是无所畏惧的程序员的标志,但如果您向它提供数百MB不包含任何签名的数据,这种特殊的用法很容易破坏您的VM。

相关内容

  • 没有找到相关文章

最新更新