资讯动态

RE2 正则表达式库实战指南:安全优先的线性时间匹配引擎及其 C++ API 与安装部署

发布时间:2026/9/24 16:33:53 来源:尧图企业网站定制
RE2 正则表达式库实战指南安全优先的线性时间匹配引擎及其 C API 与安装部署【免费下载链接】re2RE2 is a fast, safe, thread-friendly alternative to backtracking regular expression engines like those used in PCRE, Perl, and Python. It is a C library.项目地址: https://gitcode.com/gh_mirrors/re21/re2RE2 是一个以安全性为首要目标的高效正则表达式 C 库自 2006 年起在 Google 及众多生产环境中投入使用。它以匹配时间与输入长度成线性关系为核心保证能够安全地处理来自不可信用户的正则表达式。本文将围绕仓库根目录下的 README.md 展开完整介绍 RE2 的设计哲学、POSIX/Perl 双语法模式、FullMatch/PartialMatch/Consume等匹配接口、RE2::Options配置体系并结合 re2/re2.h、Makefile、CMakeLists.txt 等源码给出 GNU make、CMake、Bazel 三种安装方式的完整步骤读完本文你将掌握 RE2 的核心 API 用法与底层实现原理能够在自己的 C 项目中安全、高效地接入并配置 RE2。为什么需要 RE2安全性优先的线性时间保证正如 README.md 开篇所强调的——Safety is RE2s primary goal安全性是 RE2 的首要目标。RE2 从设计之初就明确了一个目标能够无风险地处理来自不可信用户的正则表达式。其核心保证之一是匹配时间与输入字符串的长度呈线性关系linear in the length of the input string。这一保证从实现层面由三个关键机制共同支撑参见 re2/re2.h 中RE2::Options的注释第 642–668 行可配置的内存预算configurable budget解析器parser、编译器compiler与各执行引擎execution engines都在一个可配置的内存上限内工作预算耗尽时优雅失败failing gracefully而不是失控膨胀避免递归eschewing recursionRE2 不依赖递归从根本上规避了栈溢出风险DFA/NFA 双引擎与回退机制每个 RE2 对象会编译出正向与反向两个 Prog程序并可为每个 Prog 维护 DFA 缓存一旦 DFA 缓存超出预算被频繁刷新RE2 会自动回退到 NFA 实现继续执行。在 re2/re2.h 中默认内存预算常量被定义为kDefaultMaxMem 820即 8 MiB第 671 行。README 也明确指出处理来自不可信用户的表达式是 RE2 的设计初衷这让它特别适合 Web 防火墙、输入校验、日志过滤等面向外部输入的场景。设计哲学悲观求稳而非盲目求快README 坦诚地说明RE2 的目标并不是在所有情况下都比其他引擎更快。虽然它保证了渐近线性asymptotically linear的匹配时间但更复杂的表达式会带来更大的常数因子更长的表达式也会增加安全处理的额外开销。README 用悲观主义 vs 乐观主义做了精辟的类比回溯引擎如 PCRE、Perl、Python 的 re是乐观的它顺序测试每个备选分支当第一个分支命中率最高时表现最快RE2 是悲观的它并行地评估所有备选分支避免了最后一个分支慢的惩罚代价是固定的额外开销——正是这种悲观换来了 RE2 的安全性。因此 RE2 还坚持另一项原则不支持任何目前仅存在回溯解法的构造。具体而言反向引用backreferences和环视断言look-around assertions不被支持子匹配提取 submatching 不受影响。这一点在 re2/re2.h 的语法说明注释第 13–21 行中同样得到印证backreferences and generalized assertions are not available。关于正则表达式理论背景README 推荐了 Russ Cox 关于正则匹配的三篇经典文章《Regular Expression Matching Can Be Simple And Fast》《Regular Expression Matching: the Virtual Machine Approach》《Regular Expression Matching in the Wild》此处不再赘述。支持的语法POSIX 模式与 Perl 模式根据 README 的Syntax一节RE2 支持两套语法模式模式语法范围匹配语义POSIX 模式标准 POSIXegrep语法返回最左最长leftmost-longest匹配Perl 模式默认大多数 Perl 运算符返回与 Perl 相同的匹配结果被排除的仅有那些需要回溯及其潜在指数级运行时间才能实现的运算符包括反向引用和广义断言generalized assertions。仓库内附有完整的语法参考文档 doc/syntax.txt及其 HTML 版本 doc/syntax.html详细记录了 Perl 模式下受支持的全部语法。此外re2/re2.h 的注释给出了几个最常见的扩展运算符示例C 普通字符串字面量中需使用双反斜杠hello (\\w) world // \w 匹配一个单词字符 version (\\d) // \d 匹配一个数字 hello\\sworld // \s 匹配任意空白字符 \\b(\\w)\\b // \b 在单词边界处匹配非空字符串 (?i)hello // (?i) 开启大小写不敏感匹配 /\\*(.*?)\\*/ // .*? 尽可能少地匹配如果使用 C11 原始字符串字面量raw string literal则无需转义R(hello (\w) world) R(version (\d)) R(hello\sworld) R(\b(\w)\b) R((?i)hello) R(/\*(.*?)\*/)C API 实战RE2 的原生语言是 Cre2/re2.h 是核心头文件总接口约 1074 行完整公开接口可直接阅读该头文件。下面按 README 的章节逐一展开并结合头文件补充实现细节。基本匹配操作FullMatch 与 PartialMatchRE2 提供两个基本操作符定义见 re2/re2.h 第 411–429 行RE2::FullMatch要求正则表达式与整个输入文本完全匹配RE2::PartialMatch在输入文本的某个子串中寻找匹配POSIX 模式下返回最左最长匹配Perl 模式下返回与 Perl 相同选择的匹配。README 给出的示例assert(RE2::FullMatch(hello, h.*o)) assert(!RE2::FullMatch(hello, e)) assert(RE2::PartialMatch(hello, h.*o)) assert(RE2::PartialMatch(hello, e))从 re2/re2.h 的实现看FullMatch/PartialMatch内部通过变参模板variadic templates把每个参数包装成RE2::Arg对象再调用FullMatchN/PartialMatchN数组版接口执行第 348–368、410–429 行这是下文可变参数匹配一节的基础。子匹配提取Submatch Extraction两个匹配函数都接受额外指针参数用于存放子匹配结果。README 说明参数类型可以是string*整数类型absl::string_view*与std::string_view非常相似因历史原因 RE2 使用前者string_view本质上是指向原始输入文本的指针 长度计数行为类似字符串但不携带自身存储。**注意**与使用指针类似一旦原始文本被删除或超出作用域就不能再使用该string_view。README 给出的完整示例含多种成功/失败路径// 成功解析。 int i; string s; assert(RE2::FullMatch(ruby:1234, (\\w):(\\d), s, i)); assert(s ruby); assert(i 1234); // 失败ruby 不能被解析为整数。 assert(!RE2::FullMatch(ruby, (.), i)); // 成功不提取数字。 assert(RE2::FullMatch(ruby:1234, (\\w):(\\d), s)); // 成功跳过 NULL 参数。 assert(RE2::FullMatch(ruby:1234, (\\w):(\\d), (void*)NULL, i)); // 失败整数溢出导致值无法存入 i。 assert(!RE2::FullMatch(ruby:123456789123, (\\w):(\\d), s, i));结合 re2/re2.h 的接口注释第 384–409 行可提取参数类型可总结为参数类型行为std::string匹配片段被拷贝进字符串absl::string_view被修改为指向匹配片段std::optionalT处理可选子模式未出现的情况如(\d)?未匹配时各种标量数值类型文本按十进制解析后存入定义了bool T::ParseFrom(const char*, size_t)的类型调用其ParseFrom解析(void*)NULL忽略对应的捕获子串需要特别注意的是README 与头文件注释均强调FullMatch返回true当且仅当同时满足三个条件——(a) 文本与正则完全匹配(b) 匹配到的子模式数量≥提供的指针参数数量(c) 第 i 个参数类型能容纳第 i 个捕获子串。另外可选子模式未匹配时被赋值为空串null string因此RE2::FullMatch(abc, [a-z](\\d)?, number)会返回false——因为不存在字符串连空串都不是无法解析为数字此时应改用std::optionalintre2/re2.h 第 401–409 行。预编译正则表达式RE2 对象复用上述示例每次调用都会重新编译正则表达式。性能敏感场景下可以一次性编译为RE2对象并反复复用README 明确指出预编译后通常能比sscanf更快地解析文本RE2 re((\\w):(\\d)); assert(re.ok()); // 编译成功若失败可查看 re.error(); assert(RE2::FullMatch(ruby:1234, re, s, i)); assert(RE2::FullMatch(ruby:1234, re, s)); assert(RE2::FullMatch(ruby:1234, re, (void*)NULL, i)); assert(!RE2::FullMatch(ruby:123456789123, re, s, i));从 re2/re2.h 看RE2对象被明确设计为多线程并发安全An RE2 object is safe for concurrent use by multiple threads第 237–238 行且不可拷贝、不可移动第 292–304 行注释建议需要共享时使用std::shared_ptrRE2或std::unique_ptrRE2。对象还提供一组诊断接口第 306–335 行re.ok()是否成功创建re.pattern()原始模式串re.error()/re.error_code()/re.error_arg()失败时的错误信息、错误码ErrorCode枚举第 248–269 行覆盖ErrorBadEscape、ErrorBadCharClass、ErrorMissingParen、ErrorBadUTF8、ErrorPatternTooLarge等与出错片段ProgramSize()/ReverseProgramSize()/ProgramFanout()程序大小与扇出直方图近似衡量正则的成本。进阶匹配接口Consume、FindAndConsume 与 Match除了两个基本操作符re2/re2.h 还提供增量扫描接口RE2::Consume(input, pattern, ...)第 444–447 行要求正则匹配文本的前缀成功后input前进到匹配文本之后可用于反复解析var value这样的行流std::string contents ...; // 填充字符串 absl::string_view input(contents); // 用 string_view 包装 std::string var; int value; while (RE2::Consume(input, (\\w) (\\d)\n, var, value)) { ...; }头文件特别提醒如果正则可能匹配空字符串循环体会无限空转必须自行检查空匹配并推进或跳出循环第 162–167 行。RE2::FindAndConsume(input, pattern, ...)第 462–465 行与Consume类似但不锚定在开头例如反复调用RE2::FindAndConsume(input, (\\w), word)可提取字符串中的所有单词。RE2::Match(text, startpos, endpos, anchor, submatch, nsubmatch)第 585–590 行最通用的底层匹配例程支持指定起始/结束偏移与锚定方式Anchor枚举UNANCHORED/ANCHOR_START/ANCHOR_BOTH第 542–546 行并直接填充absl::string_view数组。头文件提示请求的子匹配信息越少运行越快nsubmatch 0最快nsubmatch 1次之且失败时submatch[]也可能被改写。Options预定义选项与完整配置体系RE2构造函数接受可选的第二参数来改变默认选项。README 首先介绍了三个预定义选项对应 re2/re2.h 的CannedOptions枚举第 276–281 行RE2 re((ab, RE2::Quiet); // 解析失败时不向 stderr 输出错误 assert(!re.ok()); // 仍可通过 re.error() 查看详情RE2::Quiet静默解析错误不打印日志默认log_errors为 true 时会输出RE2::Latin1禁用 UTF-8按 Latin-1 解释文本与模式RE2::POSIX使用 POSIX 语法与最左最长匹配。如果需要更细粒度控制可以自己声明RE2::Options对象并逐项配置。完整选项清单与默认值定义在 re2/re2.h 第 618–755 行的RE2::Options类中选项默认值含义utf8true文本与模式按 UTF-8 解释否则按 Latin-1posix_syntaxfalse将正则限制为 POSIX egrep 语法longest_matchfalse搜索最长匹配而非首个匹配log_errorstrue语法与执行错误记录到 ERROR 日志max_mem8208 MiBRE2 的大致最大内存占用literalfalse将字符串按字面量解释而非正则never_nlfalse即使模式中有\n也永不匹配换行dot_nlfalse.匹配包括换行在内的一切never_capturefalse所有括号都解析为非捕获组case_sensitivetrue大小写敏感匹配正则内可用(?i)覆盖POSIX 模式下除外perl_classesfalse允许 Perl 的\d \s \w \D \S \W仅 POSIX 模式下生效word_boundaryfalse允许 Perl 的\b \B仅 POSIX 模式下生效one_linefalse^与$仅匹配文本首尾仅 POSIX 模式下生效其中perl_classes、word_boundary、one_line三项只在posix_syntax true时被参考非 POSIX 模式下这些特性始终开启且无法关闭此时要做多行匹配需在正则开头加(?m)。关于max_memre2/re2.h 的注释给出了非常具体的预算分配机制第 642–668 行每个 RE2 拥有两个 Prog正向、反向每个 Prog 可带两个 DFA首个匹配、最长匹配共 4 个 DFA预算在两者间静态分配2/3 给正向 Prog1/3 给反向 Prog正向 Prog 将其余量的一半分给每个 DFA反向 Prog 全部给它的最长匹配 DFA一旦某个 DFA 填满预算会清空缓存重新开始若这发生得过于频繁RE2 就回退到 NFA 实现。这一机制是内存可控、优雅失败设计承诺的直接代码级证据。可变参数匹配FullMatchN 系列当正则表达式是运行时动态计算、捕获组数量无法在写代码时确定时可以使用数组版N 后缀接口re2/re2.h 第 348–355 行const RE2::Arg* args[10]; int n; // ... 用指向 RE2::Arg 对象的指针填充 args ... // ... 将 n 设为 RE2::Arg 对象的数量 ... bool match RE2::FullMatchN(input, pattern, args, n);它等价于RE2::FullMatch(input, pattern, *args[0], *args[1], ..., *args[n - 1])。PartialMatchN、ConsumeN、FindAndConsumeN同理。解析十六进制、八进制与 C 进制数字默认情况下向匹配函数传入数值类型指针时文本按十进制解析。如需其他进制可用三个运算符包装指针re2/re2.h 第 196–209、762–766 行int a, b, c, d; RE2::FullMatch(100 40 0100 0x40, (.*) (.*) (.*) (.*), RE2::Octal(a), RE2::Hex(b), RE2::CRadix(c), RE2::CRadix(d)); // 结果a、b、c、d 均为 64RE2::Hex(ptr)按基 16 解析RE2::Octal(ptr)按基 8 解析RE2::CRadix(ptr)遵循 C 语言风格前缀——0开头按八进制、0x开头按十六进制、否则按十进制。替换、提取与转义Replace / GlobalReplace / Extract / QuoteMetare2/re2.h 还提供了字符串变换接口第 467–520 行RE2::Replace(str, re, rewrite)替换第一个匹配。rewrite 中\1至\9可引用对应捕获组\0表示整个匹配文本。例如std::string s yabba dabba doo; RE2::Replace(s, b, d); // s 变为 yada dabba dooRE2::GlobalReplace(str, re, rewrite)替换所有不重叠的匹配banana中替换ana只会发生一次返回替换次数。如yabba dabba doo中替换b为d得yada dada doo。RE2::Extract(text, re, rewrite, out)提取匹配文本并应用 rewrite 写入outtext不得与*out别名。RE2::QuoteMeta(unquoted)转义字符串中所有正则元字符返回值作为正则使用时精确匹配原字符串例如1.5-2.0?转义为1\.5\-2\.0\?。配合使用前可用CheckRewriteString校验 rewrite 串的合法性用MaxSubmatch获取 rewrite 需要的最大捕获组编号第 592–615 行。Unicode 规范化说明README 专门指出RE2 按Unicode 码点code point操作不做任何规范化normalization。例如正则/ü/U00FC带分音符的 u不会匹配输入üU0075 U0308u 后跟组合分音符。如果你确实需要此类匹配最简单的方案是在使用 RE2 之前对正则与输入做一次预处理规范化Unicode 规范化规范详见 Unicode TR15。附加提示LazyRE2、RE2::Set、PossibleMatchRange 与 hooksREADME 指引高级用法构造自定义参数列表、把 RE2 用作词法分析器、解析十六/八/C 进制数字等参见 re2/re2.h该头文件还包含若干值得注意的进阶设施LazyRE2第 986–1019 行安全地编写全局/静态 RE2 的辅助类型。用static LazyRE2 re {.*};替代static RE2 re(.*);前者借助absl::call_once保证多线程安全且设计上永不析构其构造的 RE2适用于全局与函数静态变量static LazyRE2 re {.*}; // 之后用 *re 代替 re 使用RE2::Set定义于 re2/set.h一个同时搜索多组正则的集合类型。先Add(pattern, error)添加返回从 0 开始递增的索引解析失败返回 -1再Compile()编译最后Match(text, v)一次性获得所有命中正则的索引列表结果无序。常用于防火墙规则匹配、多关键字过滤等场景。顺带一提re2/stringpiece.h 中re2::StringPiece目前是absl::string_view的别名老代码可继续通过该头文件使用。PossibleMatchRange(min, max, maxlen)第 536–537 行计算所有可能匹配字符串的字典序范围可用于预过滤器。hooks命名空间第 1021–1067 行通过SetDFAStateCacheResetHook/SetDFASearchFailureHook挂钩 DFA 缓存重置与搜索失败事件便于观测引擎行为。安装与构建make、CMake、Bazel 三选一README 说明 RE2 可以用GNU make、CMake 或 Bazel构建与安装。构建 RE2 需要C17 编译器与Abseil库构建测试与基准需要GoogleTest与Benchmark。使用 GNU make最简安装流程README 原文make make test make benchmark make install make testinstall从 Makefile 可以看到更多细节编译参数硬性要求-stdc17 -pthread第 52 行且通过pkg-config自动发现 Abseil 依赖ABSL_DEPS第 6–21 行静态库与动态库并行产出obj/libre2.a与obj/so/libre2.$(SOEXT)第 110 行动态库的 ABI 版本SONAME为11第 85 行共享库符号受 libre2.symbolsLinux/SunOS与 libre2.symbols.darwinmacOS约束make test会构建并运行 debug、static、shared 三套测试第 288–301 行通过仓库自带的 runtests 脚本执行make benchmark构建obj/test/regexp_benchmark源码位于 re2/testing/regexp_benchmark.ccmake fuzz可构建 re2/fuzzing/re2_fuzzer.cc 模糊测试器make testinstall会基于 testinstall.cc 编译两个小程序分别链接静态库与动态库并运行验证安装后的库确实可用第 350–384 行。获取依赖README 给出的各平台命令Linuxapt install libabsl-dev libgtest-dev libbenchmark-devmacOSbrew install abseil googletest google-benchmark pkg-config-wrapperWindowsvcpkg install abseil gtest benchmark或vcpkg add port abseil gtest benchmark使用 CMake如果标准 Makefile 在依赖查找上遇到麻烦切换 CMake 往往能解决rm -rf build cmake -DRE2_TESTON -DRE2_BENCHMARKON -S . -B build cd build make make test make installCMake 相关说明源自 README 与 CMakeLists.txt 第 16–36 行的option()声明RE2_TESTON构建并运行测试RE2_BENCHMARKONmake test会构建并运行测试二进制同时构建regexp_benchmark基准二进制但不运行它若完全不需要测试与基准可省略对应-D参数此时也不需要 GoogleTest 与 Benchmark 依赖RE2_USE_ICUON引入 ICU Unicode 库依赖同时扩展\p与\P模式可用的属性名列表Makefile 第 30–33 行也预留了CCICU/LDICU的同等开关CMake 还可生成Visual Studio 与 Xcode 工程以及 Cygwin、MinGW、MSYS 的 makefile。注意事项Visual Studio 用户需要 2019 或更高版本Cygwin 用户必须从 Cygwin 命令行运行 CMake而非 Windows 命令行。若要将 RE2 引入你自己的 CMake 工程README 提示 CMake 依赖有两种方式add_subdirectory()依赖的源码位于你工程的子目录中与find_package()依赖的二进制已安装到系统上无论哪种方式target_link_libraries(... re2::re2)都可以直接使用。工程级配置还可在 re2.pc.inpkg-config 模板与 re2Config.cmake.in 中查看。使用 Bazel如果使用 Bazel依赖包括 Abseil会由构建系统自动处理你只需下载 Bazel 本身可用 Bazelisk 管理版本go install github.com/bazelbuild/bazelisklatest # 或 macOSbrew install bazelisk bazelisk build :all bazelisk test :all仓库根目录提供了 WORKSPACE.bazel、WORKSPACE.bzlmod、MODULE.bazel 与 BUILD.bazel 等 Bazel 工程文件。若从其他项目使用 RE2需要确保至少使用C17。此外用源码内附的 app 目录TypeScript Rollup 的最小示例可从另一角度观察 RE2 的绑定使用方式但注意它并非官方主推的绑定。Python 官方封装google-re2README 明确指出RE2 的官方 Python 封装位于仓库 python 目录并发布在 PyPI 上名为google-re2。注意PyPI 上还有一个re2包但它并非 RE2 作者维护且已停止维护请使用google-re2。从 python/re2.py 看该模块是 Python 标准re模块的即插即用替代品drop-in replacement底层通过 pybind11 绑定 python/_re2.cc。关键特性同样不支持反向引用、环视断言等需要回溯的特性已知差异之一是 PCRE 的\Z需要改写为 RE2 的\zREADME 见 python/README差异清单见 python/re2.py 第 1–27 行Options类以可读写属性的形式暴露 RE2 的全部 13 项选项max_mem、encoding、posix_syntax、longest_match、log_errors、literal、never_nl、dot_nl、never_capture、case_sensitive、perl_classes、word_boundary、one_line第 45–59 行API 对齐re模块compile/search/match/fullmatch/finditer/findall/split/sub/subn/escape第 62–120 行模块内部维护一个LRU 缓存最多 128 个已编译正则对象每个对象的底层 RE2 默认占用至多 8 MiB因此缓存默认至多约 1 GiB实际通常远小于此第 23–26 行。构建该封装需要 Python 3、pybind11以及系统已安装的 RE2Debian 下为libre2-dev细节见 python/setup.py 与 python/README。端口与封装一览RE2 以 C 实现官方 Python 封装即上文所述google-re2。除此之外社区还提供了众多非官方封装与移植README 中列出的包括此处仅列名称官方维护的移植另有说明Ccre2Dre2dDUB 上发布Erlangre2Hex 上发布Infernoinferno-re2Node.jsnode-re2NPM 上发布OCamlre2OPAM 上发布Janestreet 维护Perlre-engine-RE2CPAN 上发布Rre2CRAN 上发布Rubyre2RubyGems 上发布WebAssemblyre2-wasmNPM 上发布RE2JRE2 C 代码到纯 Java 的移植RE2JSRE2J 到 JavaScript 的移植Go 的regexp包与 Rust 的regexcrate 虽与 RE2不共享代码但遵循相同原理、接受相同语法并提供相同的效率保证。测试与验证RE2 附带极其完备的测试套件re2/testing 目录覆盖解析re2/testing/parse_test.cc、编译re2/testing/compile_test.cc、DFAre2/testing/dfa_test.cc、搜索re2/testing/search_test.cc、字符类re2/testing/charclass_test.cc、正则化简re2/testing/simplify_test.cc、API 参数re2/testing/re2_arg_test.cc等另有 exhaustive 系列re2/testing/exhaustive_test.cc 及 exhaustive1/2/3对海量正则-文本组合做穷举验证以及 re2/testing/random_test.cc 随机测试。核心 API 的行为断言如子匹配、整数溢出失败、NULL跳过等与 README 中的示例完全一致可直接在 re2/testing/re2_test.cc 中找到对应用例作为印证。更多仓库内资料完整语法参考doc/syntax.txt、doc/syntax.html核心 API 头文件re2/re2.h、re2/set.h构建配置Makefile、CMakeLists.txt、BUILD.bazel、MODULE.bazel封装与测试python、re2/testing、runtests、testinstall.cc。关于社区讨论与代码变更动态README 推荐使用官方 issue 跟踪器与 re2-dev 邮件列表向项目提交变更前请先阅读贡献指南并注意 RE2 项目不使用 GitHub pull request作为合入方式。【免费下载链接】re2RE2 is a fast, safe, thread-friendly alternative to backtracking regular expression engines like those used in PCRE, Perl, and Python. It is a C library.项目地址: https://gitcode.com/gh_mirrors/re21/re2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价