我正在编写解析文本文件的代码。我的库的用户可以将行分隔符作为regex传递。
文件可能很大,所以我需要流式传输内容。
所以问题是,当流通过我的行匹配器时,我如何将正则表达式应用于流。
我将应用一个限制,以便正则表达式匹配的行分隔符不能超过100个字符,否则正则表达式有可能匹配文件的全部内容。
我不能只缓冲最多100个字符,因为分隔符可能会跨越缓冲区。
我能想到的唯一想法是将正则表达式准备成段,并在执行过程中检查部分匹配。
有更好的主意吗?
发布于 2019-12-16 15:16:27
您的文件很大,因此您不希望首先将其完全加载到内存中。这是合理的。您确实需要缓冲其中的一部分,至少是最后检测到的行分隔符之后的所有内容,以便您可以将其与下一个分块结合,以便查找可能在分块之间拆分的分隔符。
这将是我最初的方法:保留一个“前缀”字符串,即最后一个行分隔符之后的所有内容,当您收到新的块时,将其连接到前缀上,然后在整个可用字符串中检查行分隔符。如果前缀超过100个字符,您可以将前缀拆分为绝对不属于分隔符的部分,然后将其直接放入StringBuffer中,并将最后99个字符与下一个块合并。我会对此进行基准测试,因为很难看出它是否会比连接整个代码更快,但如果您得到跨越多个块的行,则可能会更快。
如果您允许任意的RegExps,那么就没有更简单的解决方案了。(即使这样也不允许RegExp使用前瞻或后视来检查100个字符匹配之外的字符,甚至可能是在更早或更晚的块中,您确实需要将内存中的整个文件作为字符串才能使这种恶作剧起作用)。
现在,如果这样做效率太低,可能是因为某些行太大,或者某些块太小,而您正在进行大量复制来进行连接,那么我将不再使用RegExps (并且您应该使用Pattern,而不是RegExp,作为您已经接受的类型),并开始只使用字符串或代码单元序列来搜索。
然后,将每个传入的块扫描到最后,并记住您是否看到了部分分隔符,以及其中有多少分隔符,然后您就可以继续下一个块,而不需要首先在内存中组合它们来对组合运行RegExp。它甚至允许您在传入的字节中搜索分隔符,而不是首先将它们转换为字符串,从而进一步减少复制开销。
例如,使用\r\n\r\n\t作为分隔符,如果您在一个块的末尾看到过\r\n\r\n,那么您需要能够识别下一个块开始时的\t和\r\n\t。(您可以修改像KMP string search这样的东西来达到这个目的,或者只是不允许使用不太简单的行分隔符)。
如果您有一个基于状态机的RegExp实现,那么将状态保持在一个块的末尾并在下一个块中继续匹配将是微不足道的,但是Dart (JavaScript) RegExp不是这样的实现,并且它不是用来进行部分匹配的。我不建议将RegExp本身转换为与原始文件匹配的前缀,并检测哪个前缀。很简单,因为它是very non-trivial。这是用于实际上是常规的RegExp,而Dart不是(反向引用是非常规的)。
https://stackoverflow.com/questions/59351376
复制相似问题