资讯详情

资讯详情

ripgrep 的行式搜索核心:深入 grep-searcher 的 Searcher、Sink 与二进制检测机制

ripgrep 的行式搜索核心深入 grep-searcher 的 Searcher、Sink 与二进制检测机制【免费下载链接】ripgrepripgrep recursively searches directories for a regex pattern while respecting your gitignore项目地址: https://gitcode.com/GitHub_Trending/ri/ripgrepcrates/searcher/README.md 介绍了 ripgrep 仓库中的grep-searchercrate——一个用于执行快速行式搜索的高层库负责上下文行报告、行数计数、搜索反转、二进制数据检测、自动 UTF-16 转码以及内存映射mmap策略决策等关键行为。本文以该文档为主体逐条展开其描述的每项能力并结合仓库中 crates/searcher 的源码实现与示例帮助你既能在自己的 Rust 项目中使用它也能理解 ripgrep 搜索管线的底层工作原理。一、定位行式搜索的高层库grep-searcher的官方描述见 crates/searcher/Cargo.toml是 Fast line oriented regex searching as a library即作为库提供的快速行式正则搜索。README 明确列出了它统一承担的职责报告上下文行contextual lines计数行号counting lines反转搜索inverting a search对应grep -v的语义检测二进制数据detecting binary data自动 UTF-16 转码automatic UTF-16 transcoding决定是否使用内存映射deciding whether or not to use memory maps。这些能力全部收敛在Searcher这一核心类型上。从 crates/searcher/src/lib.rs 的 crate 文档可以看到完整的心智模型Searcher从某个数据源如文件读取字节使用Matcher如正则表达式对字节执行搜索并把结果报告给Sink如 stdout。Matcher本身定义在 grep-matcher crate 中接口与正则表达式非常相似grep-regexcrates/regex提供了基于 Rustregexcrate 的实现。README 还给出了一条重要建议不要直接依赖grep-searcher而应优先使用门面facadecrategrep——它位于 crates/grep/src/lib.rs对外统一暴露Matcher、Searcher、Sink及相关配置类型屏蔽底层各 crate 的细节。二、快速上手依赖声明与最小示例2.1 声明依赖继承自 README 的用法部分在你的项目Cargo.toml中添加[dependencies] grep-searcher 0.1当前仓库中该 crate 的实际版本为0.1.17见 crates/searcher/Cargo.toml0.1这一 semver 简写可以解析到它。其运行依赖包括memchr、encoding_rs、encoding_rs_io、bstr、memmap2等见 crates/searcher/Cargo.toml。2.2 最小搜索示例crates/searcher/src/lib.rs 中的文档示例演示了执行搜索并用UTF8sink 收集结果的完整流程use { grep_matcher::Matcher, grep_regex::RegexMatcher, grep_searcher::Searcher, grep_searcher::sinks::UTF8, }; const SHERLOCK: static [u8] b\ For the Doctor Watsons of this world, as opposed to the Sherlock Holmeses, success in the province of detective work must always be, to a very large extent, the result of luck. Sherlock Holmes can extract a clew from a wisp of straw or a flake of cigar ash; but Doctor Watson has to have it taken out for him and dusted, and exhibited clearly, with a label attached. ; let matcher RegexMatcher::new(rDoctor \w)?; let mut matches: Vec(u64, String) vec![]; Searcher::new().search_slice(matcher, SHERLOCK, UTF8(|lnum, line| { // We are guaranteed to find a match, so the unwrap is OK. let mymatch matcher.find(line.as_bytes())?.unwrap(); matches.push((lnum, line[mymatch].to_string())); Ok(true) }))?; assert_eq!(matches.len(), 2); assert_eq!(matches[0], (1, Doctor Watsons.to_string())); assert_eq!(matches[1], (5, Doctor Watson.to_string()));要点Searcher::new()使用默认配置构建搜索器等价于SearcherBuilder::new().build()见 crates/searcher/src/searcher/mod.rssearch_slice直接对内存字节切片执行搜索sinks::UTF8是sink.rs中sinks子模块提供的闭包式便捷实现回调收到行号lnum与行内容返回Ok(true)继续搜索、Ok(false)停止搜索。仓库还提供了命令行示例 crates/searcher/examples/search-stdin.rs从标准输入读取数据用命令行参数作为正则模式通过search_reader执行搜索并打印行号:行内容是search_readerAPI 的最短可用样板。三、三大抽象Searcher、Matcher、Sink3.1 Searcher搜索的执行者Searcher定义于 crates/searcher/src/searcher/mod.rs内部持有四部分状态字段作用config: Config全部搜索配置行终止符、上下文、mmap 策略等decode_builder/decode_buffer转码流构建器与转码临时缓冲区无需转码时字节零开销直通line_buffer: RefCellLineBuffer行式搜索使用的滚动缓冲见 line_buffer.rsmulti_line_buffer多行搜索时的整段内容缓冲对外的四个搜索入口按数据源区分search_pathmod.rs#L643-L657按路径打开文件并搜索search_filemod.rs#L665-L676对已打开的File搜索search_readermod.rs#L727-L765对任意std::io::Read搜索search_slicemod.rs#L769-L795对内存切片搜索。从源码结构看搜索策略的选择逻辑很清晰文件搜索会先尝试 mmapsearch_file_maybe_pathmod.rs#L678-L714mmap 不可用时若启用了多行搜索则把整个文件预读到堆上MultiLine策略否则回退到通用的逐行滚动缓冲搜索ReadByLine。而search_slice在无需转码时走最快的SliceByLine路径否则委托给search_reader。3.2 Sink结果的推式接收器grep-searcher采用push推执行模型搜索器驱动执行把结果推给调用方提供的Sink实现而不是由调用方拉取结果见 crates/searcher/src/sink.rs 的 trait 文档。Sinktraitsink.rs#L102-L223的方法及其默认行为方法何时被调用默认行为matched发现匹配时必须实现context发现上下文行时忽略返回Ok(true)context_break上下文行组之间出现间隔时忽略binary_data启用二进制检测且发现二进制数据时忽略begin搜索开始前什么都不做finish搜索成功完成后什么都不做每个方法返回Ok(false)时搜索立即停止随后调用finish返回错误时搜索立即停止且不再调用finish错误上抛。错误类型由伴随的SinkErrortraitsink.rs#L18-L60描述std::io::Error与Boxdyn std::error::Error都开箱即用地实现了它文档建议一般直接用std::io::Error即可。匹配结果的载体是SinkMatchsink.rs#L366-L426bytes()匹配行的完整字节含行终止符lines()行迭代器——多行搜索时可能跨越多行absolute_byte_offset()匹配起点在整个输入中的绝对字节偏移不能当作内存切片下标使用;line_number()首行行号仅当构建器开启了行号计数时才有值buffer()与bytes_range_in_buffer()暴露底层搜索缓冲及其对应区间供需要窗口信息的实现者使用。搜索结束时的汇总信息是SinkFinishsink.rs#L331-L362提供byte_count()共搜索了多少字节和binary_byte_offset()首个二进制字节的绝对偏移。四、SearcherBuilder逐项解析全部配置README 概括的六项能力在实现层面正是SearcherBuilder的一组链式配置方法。内部配置结构体Config及其默认值定义在 crates/searcher/src/searcher/mod.rs逐条对应如下Builder 方法配置字段默认值说明line_terminatorline_termb\n行终止符matcher 若自行指定行终止符必须与之一致否则构建时报ConfigError::MismatchedLineTerminatorsinvert_matchinvert_matchfalse反转匹配报告不匹配的行line_numberline_numbertrue是否计算行号关闭可省掉一点性能开销after_contextafter_context0每个匹配后报告的上下文行数before_contextbefore_context0每个匹配前报告的上下文行数passthrupassthrufalse直通模式把全部不匹配行都当作上下文行相当于无界前后上下文启用时before_context/after_context被强制置0见build()mod.rs#L315-L320heap_limitheap_limitNone堆内存上限设为0时仅允许 mmap 策略可用否则立即报错memory_mapmmapNevermmap 策略见下节binary_detectionbinaryBinaryDetection::none()二进制检测策略见下节encodingencodingNone显式指定源数据编码无条件转码为 UTF-8bom_sniffingbom_sniffingtrue基于 BOM 的自动转码multi_linemulti_linefalse允许多行匹配代价是必须整体载入内容stop_on_nonmatchstop_on_nonmatchfalse在匹配行之后出现不匹配行时停止搜索适合匹配项集中在相邻行的有序文件max_matchesmax_matchesNone最多产出的匹配数0是合法值意味着立即停止几个值得展开的默认值设计mmap 默认关闭Config::default()中mmap: MmapChoice::default()而MmapChoice的Default实现是Nevermmap.rs#L21-L25。Builder 文档直言与常规直觉相反mmap 并不总能带来更快搜索mod.rs#L495-L501行号默认开启这是与搜索库直觉相反但贴合 grep 语义的选择heap_limit的细分行为限制固定缓冲搜索时滚动缓冲的容量上限单行超长则报错多行搜索时约束整段内容的堆占用。当限制设为0且 mmap 不可用时构建结果会体现为ConfigError::SearchUnavailablemod.rs#L244-L262。构建搜索器时build()mod.rs#L315-L337还会根据encoding/bom_sniffing组装DecodeReaderBytesBuilder并预留 8KB 的转码临时缓冲区文档同时建议构建后的Searcher应尽量复用。五、二进制检测三种策略与两种搜索路径的差异README 提到的 detecting binary data 由BinaryDetection实现crates/searcher/src/searcher/mod.rs共三种策略构造器行为BinaryDetection::none()默认不做检测Sink 可能收到任意字节BinaryDetection::quit(byte)检测到指定字节ripgrep 场景中通常是 NUL即停止搜索如同到达 EOFBinaryDetection::convert(byte)把指定字节替换为行终止符CRLF模式下替换为LF调用方保证不会观察到该字节仅在固定缓冲搜索下生效quit_byte()/convert_byte()两个访问器允许Sink实现按策略做差异化处理。源码注释mod.rs#L43-L53特别解释了二进制检测在两类搜索路径下的差异固定缓冲搜索检测应用于缓冲内容的填充过程——因为二进制文件可能完全没有行终止符若不在缓冲层直接检测可能导致内存暴涨mmap/堆上搜索检测只保证覆盖匹配所在部分启用Quit时会先扫描开头前几个 KB任何后续匹配或上下文行中一旦检测到二进制数据搜索同样按 EOF 处理。quit策略触发时Sink的binary_data回调会收到首个二进制字节的绝对偏移最终也体现在SinkFinish::binary_byte_offset()中——这就是 ripgrep 对二进制文件输出 Binary file ... matches 之类提示的底层数据来源之一。六、内存映射策略默认 Never谨慎 AutoMmapChoicecrates/searcher/src/searcher/mmap.rs只有两个选项MmapChoice::never()默认永不使用内存映射多行搜索时改为把全部内容读入堆unsafe fn MmapChoice::auto()由搜索器按文件大小、平台等启发式决定是否启用 mmap。之所以是unsafe构造器是因为文件在映射期间不被修改这一契约无法在所有平台上封装进安全 API——调用方要自行承担文件被截断时进程收到SIGBUS的风险mod.rs#L492-L494。从MmapChoice::open的实现mmap.rs#L65-L115还能看到两个实现细节在 macOS 上直接放弃 mmap源码注释指出 macOS 的 mmap 表现不佳并引用了上游 issue 讨论Unix 平台成功映射后会调用madvise(Sequential)提示内核顺序读取失败仅记录 debug 日志不影响搜索。Builder 文档给出的经验结论mod.rs#L465-L490搜索大型目录时mmap 的管理开销可能反而比普通 read 更慢仅在搜索已驻留内存的超大单文件这类场景才可能略快。官方建议不确定就不要开。七、编码与 BOM 嗅探自动 UTF-16 转码README 提到的 automatic UTF-16 transcoding 对应Encoding与bom_sniffing两项配置mod.rs#L129-L146、mod.rs#L518-L556Encoding::new(label)按 WHATWG Encoding Standard 的标签表解析编码未知标签返回ConfigError::UnknownEncoding显式设置编码源数据被无条件转码为 UTF-8若存在 BOM则以 BOM 声明的编码优先。转码错误字节替换为 Unicode 替换码点UFFFD搜索不会因坏字节中断未设置编码默认开启 BOM 嗅探时UTF-16含 BOM文件会被无缝识别并转码后搜索找不到 BOM 时则按当作 UTF-8处理——只要数据至少是 ASCII 兼容的搜索仍能产出有用结果。search_slice中的slice_needs_transcoding分支mod.rs#L782-L787体现了这一设计只有需要转码时才退回通用 reader 路径否则享受切片直搜的快路径。八、与 ripgrep 主程序的关系在 ripgrep 的 crate 分层中grep-searcher处于搜索引擎层crates/grep 是门面 cratere-export 各底层类型是外部程序推荐的唯一入口与 README 的 NOTE 一致crates/printer 负责把搜索结果变成人类/机器可读的输出grep-searcher的Sink分别被 standard.rs标准 grep 风格输出含上下文行合并、json.rsJSON/JSONLines 输出、summary.rs-c计数与文件摘要、util.rs 等模块实现为各自复杂的Sink——lib.rs文档中所说的Sink 实现可以非常复杂如 grep-printer 中的 Standard printer指的就是这条链路。因此可以这样理解整条管线grep-matcher模式层→grep-searcher行式搜索 上下文/二进制/编码/mmap 策略→grep-printer输出层rg命令行通过grep门面把它们串起来。九、许可证与文档入口许可按 crates/searcher/README.md 与 crates/searcher/Cargo.toml 声明grep-searcher双重许可于MIT 或 Unlicense对应文件为 crates/searcher/LICENSE-MIT 与 crates/searcher/UNLICENSE文档完整 API 文档发布在 docs.rs 的grep-searcher页面本地可阅读 crates/searcher/src/lib.rs 顶部与 crates/searcher/src/sink.rs 中的 trait 级 rustdoc二者是理解该库最重要的两份活文档。十、实践清单结合以上源码证据使用该库时的建议优先依赖门面 crategrep而非直接依赖grep-searcher构建一次Searcher后在多次搜索间复用按数据源选择入口能拿到路径/File时优先search_path/search_file保留 mmap 可能性纯流式数据用search_reader需要上下文或计数输出时实现完整的Sink至少处理context、context_break、finish并参考 crates/printer/src/standard.rs 的成熟实现对含二进制的数据源启用BinaryDetection::quit或convert并在binary_data回调中做提示除非有明确的单大文件场景与安全性评估保持默认的MmapChoice::never()。【免费下载链接】ripgrepripgrep recursively searches directories for a regex pattern while respecting your gitignore项目地址: https://gitcode.com/GitHub_Trending/ri/ripgrep创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →