如何对Supply或其他类流序列执行正则表达式匹配?
实现流文本的跨块正则匹配
在Raku中,你完全可以实现流文本的跨块正则匹配,无需将全部文本读入内存。核心是维持匹配状态,以下是两种常用方案:
方案1:手动维护残留缓冲区
适合简单正则,手动管理未完成匹配的前缀:
# 初始化缓冲区和目标正则 my $remainder = ''; my $target-regex = /foo \d+/; # 替换为你的正则 # 处理Supply流(Channel/IO::Handle同理,先转为Supply) $your-supply.tap(-> $chunk { $remainder ~= $chunk; # 全局扫描匹配,直到无法找到更多匹配 while $remainder ~~ m:g/$target-regex/ { say "匹配到: $/"; # 这里处理匹配结果 # 更新缓冲区为当前匹配结束后的剩余文本 $remainder = $remainder.substr($/.pos + $/.chars); } # 可选:优化缓冲区大小(避免无限增长) # 对于固定结构的正则,可保留最长可能的未完成前缀长度 # 比如正则是/aaa/,最多保留2个字符,超过则截断 # $remainder = $remainder.substr(*-2) if $remainder.chars > 2; });
原理:每次将新文本块拼接到残留缓冲区,扫描所有完整匹配后,保留缓冲区中未完成匹配的部分(比如fo可能和下一个块的o123组成foo123)。
方案2:使用Grammar增量解析
适合复杂正则/语法规则,Raku的Grammar原生支持增量解析,通过Cursor维持状态:
# 定义匹配规则的Grammar grammar StreamParser { token TOP { <match>+ } token match { foo \d+ } # 替换为你的匹配规则 } # 定义匹配后的处理逻辑 class MatchActions { method match($/) { say "匹配结果: $/"; # 处理匹配到的内容 } } # 初始化解析游标 my $cursor = StreamParser.parse('', :actions(MatchActions)); # 处理流中的每个文本块 $your-supply.tap(-> $chunk { $cursor = $cursor.continue($chunk); });
原理:Cursor对象会记录当前解析的状态,每次调用continue($chunk)时,会在原有状态基础上继续解析新文本块,匹配到规则时自动触发Actions中的处理方法。
适配不同流源
- IO::Handle:通过
.Supply方法转为流:$handle.Supply.tap(...) - Channel:通过
.Supply方法转为流,或直接循环读取:loop { my $chunk = $channel.receive; ... }
内容的提问来源于stack exchange,提问作者Owen
相关产品推荐
相关产品推荐

