google/re2
GitHub: google/re2
Google 开源的安全正则表达式库,通过避免回溯保证线性时间复杂度,防止 ReDoS 攻击。
Stars: 9635 | Forks: 1218
# RE2,一个正则表达式库
RE2 是一个高效、规范的 正则表达式库,
自 2006 年以来一直被 Google 和许多其他地方用于生产环境。
_**安全性是 RE2 的首要目标。**_
RE2 的设计和实现有一个明确的目标,即能够
安全地处理来自不可信用户的正则表达式。
它的主要保证之一是匹配时间与输入字符串的长度成线性关系。
它也是在考虑生产环境的基础上编写的:
解析器、编译器和执行引擎通过在可配置的预算内工作来限制其内存使用——
在耗尽时优雅地失败——并且
它们通过避免递归来防止堆栈溢出。
在所有情况下都比所有其他引擎更快并不是其目标。
虽然 RE2 保证运行时间在渐近上与
输入长度成线性关系,但更复杂的表达式可能会产生更大的常数因子;
更长的表达式会增加安全处理这些表达式所需的开销。
在某种意义上,RE2 是“悲观”的,而回溯引擎是“乐观”的:
回溯引擎按顺序测试每个分支,这使得当第一个分支很常见时速度很快。
相比之下,RE2 并行评估所有分支,避免了最后一个分支的性能惩罚,
但代价是一些开销。这种悲观主义正是 RE2 安全的原因。
实现 Perl、PCRE 和其他引擎提供的所有功能也不是其目标。
作为一项原则,RE2 不支持只存在回溯解决方案的构造。
因此,不支持反向引用和环视断言。
有关更多信息,请参阅 Russ Cox 关于正则表达式理论和实践的文章:
* [正则表达式匹配可以简单且快速](https://swtch.com/~rsc/regexp/regexp1.html)
* [正则表达式匹配:虚拟机方法](https://swtch.com/~rsc/regexp/regexp2.html)
* [野生环境下的正则表达式匹配](https://swtch.com/~rsc/regexp/regexp3.html)
### 语法
在 POSIX 模式下,RE2 接受标准 POSIX (egrep) 语法正则表达式。
在 Perl 模式下,RE2 接受大多数 Perl 运算符。唯一排除的是
那些需要回溯(及其潜在的指数级运行时)
来实现的功能。这包括反向引用(子匹配仍然可以)
和广义断言。
[语法 Wiki 页面](https://github.com/google/re2/wiki/Syntax)
详细记录了支持的 Perl 模式语法。
默认为 Perl 模式。
### C++ API
RE2 的原生语言是 C++,尽管下面列出了[移植和包装器](#ports-and-wrappers)。
#### 匹配接口
有两个基本运算符:
`RE2::FullMatch` 要求正则表达式匹配整个输入文本,并且
`RE2::PartialMatch` 查找输入文本子字符串的匹配项,
在 POSIX 模式下返回最左最长匹配,并在
Perl 模式下返回 Perl 会选择的相同匹配。
示例:
```
assert(RE2::FullMatch("hello", "h.*o"))
assert(!RE2::FullMatch("hello", "e"))
assert(RE2::PartialMatch("hello", "h.*o"))
assert(RE2::PartialMatch("hello", "e"))
```
#### 子匹配提取
两个匹配函数都接受额外的参数,子匹配将存储在其中。
参数可以是 `string*`、整数类型或 `absl::string_view*` 类型。
(`absl::string_view` 类型与 `std::string_view` 类型非常相似,
但由于历史原因,RE2 使用前者。)
`string_view` 是指向原始输入文本的指针,以及一个计数。
它的行为像字符串,但不自带存储。
就像使用指针一样,使用 `string_view` 时
必须小心,一旦原始文本被删除或超出作用域,就不要再使用它。
示例:
```
// Successful parsing.
int i;
string s;
assert(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i));
assert(s == "ruby");
assert(i == 1234);
// Fails: "ruby" cannot be parsed as an integer.
assert(!RE2::FullMatch("ruby", "(.+)", &i));
// Success; does not extract the number.
assert(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s));
// Success; skips NULL argument.
assert(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", (void*)NULL, &i));
// Fails: integer overflow keeps value from being stored in i.
assert(!RE2::FullMatch("ruby:123456789123", "(\\w+):(\\d+)", &s, &i));
```
#### 预编译正则表达式
上面的示例在每次调用时都重新编译正则表达式。
相反,您可以将其一次编译为 RE2 对象,并在每次调用中重用该对象。
示例:
```
RE2 re("(\\w+):(\\d+)");
assert(re.ok()); // compiled; if not, see 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::Quiet` 静默通常在正则表达式解析失败时
打印的错误消息:
```
RE2 re("(ab", RE2::Quiet); // don't write to stderr for parser failure
assert(!re.ok()); // can check re.error() for details
```
其他有用的预定义选项是 `Latin1`(禁用 UTF-8)和 `POSIX`
(使用 POSIX 语法和最左最长匹配)。
您还可以声明自己的 `RE2::Options` 对象,然后根据需要对其进行配置。
有关完整的选项集,请参阅[头文件](https://github.com/google/re2/blob/main/re2/re2.h)。
#### Unicode 标准化
RE2 对 Unicode 码点进行操作:它不尝试进行标准化。
例如,正则表达式 /ü/ (U+00FC, 带分音符的 u)
不匹配输入 "ü" (U+0075 U+0308, u 后跟组合分音符)。
标准化是一个漫长而复杂的话题。
如果您需要此类匹配,最简单的解决方案是在使用 RE2 之前
在预处理步骤中对正则表达式和输入进行标准化。
有关一般主题的更多详细信息,请参阅 。
#### 其他技巧和窍门
对于高级用法,例如构建您自己的参数列表,
或将 RE2 用作词法分析器,或解析十六进制、八进制和 C 进制数,
请参阅 [re2.h](https://github.com/google/re2/blob/main/re2/re2.h)。
### 安装
可以使用 GNU make、CMake 或 Bazel 构建和安装 RE2。
最简单的安装说明是:
```
make
make test
make benchmark
make install
make testinstall
```
构建 RE2 需要 C++17 编译器和 [Abseil](https://github.com/abseil/abseil-cpp) 库。
构建测试和基准测试需要
[GoogleTest](https://github.com/google/googletest)
和 [Benchmark](https://github.com/google/benchmark)。
要获取它们:
- Linux:`apt install libabsl-dev libgtest-dev libbenchmark-dev`
- macOS:`brew install abseil googletest google-benchmark pkg-config-wrapper`
- Windows:`vcpkg install abseil gtest benchmark` \
或 `vcpkg add port abseil gtest benchmark`
一旦安装了这些,构建过程必须能够找到它们。
如果标准 Makefile 遇到问题,那么切换到 CMake 会有所帮助:
```
rm -rf build
cmake -DRE2_TEST=ON -DRE2_BENCHMARK=ON -S . -B build
cd build
make
make test
make install
```
使用 CMake 且启用基准测试时,`make test` 会构建并运行测试二进制文件,
并构建 `regexp_benchmark` 二进制文件,但不会运行它。
如果您根本不需要测试或基准测试,则可以省略相应的 `-D` 参数,
这样您也不需要 GoogleTest 或 Benchmark 依赖项。
另一个有用的选项是 `-DRE2_USE_ICU=ON`,它增加了对
ICU Unicode 库的依赖,但也扩展了 `\p` 和 `\P` 模式中可用的属性名称列表。
CMake 还可用于生成 Visual Studio 和 Xcode 项目,以及
Cygwin、MinGW 和 MSYS makefile。
- Visual Studio 用户:您需要 Visual Studio 2019 或更高版本。
- Cygwin 用户:您必须从 Cygwin 命令行运行 CMake,而不是 Windows 命令行。
如果您将 RE2 添加到您自己的 CMake 项目中,
CMake 有两种使用依赖项的方法:`add_subdirectory()`,
即依赖项的**_源代码_**位于您项目的子目录中;
和 `find_package()`,即依赖项的
**_二进制文件_**已在您系统的某处构建和安装。
Abseil 文档在[这里](https://abseil.io/docs/cpp/quickstart-cmake)介绍了前者,
与[这里](https://abseil.io/docs/cpp/tools/cmake-installs)的后者进行了对比。
一旦您让 Abseil 工作,让 RE2 工作将是一个非常类似的过程,并且,
无论哪种方式,`target_link_libraries(… re2::re2)` 都应该直接可用 (Just Work™)。
如果您使用 [Bazel](https://bazel.io),它将为您处理依赖项,
尽管您仍然需要下载 Bazel,
您可以使用 [Bazelisk](https://github.com/bazelbuild/bazelisk) 进行下载。
```
go install github.com/bazelbuild/bazelisk@latest
# 或在 mac 上:brew install bazelisk
bazelisk build :all
bazelisk test :all
```
如果您从另一个项目使用 RE2,则需要确保您
至少使用 C++17。
有关示例,请参阅 RE2 [.bazelrc](https://github.com/google/re2/blob/main/.bazelrc) 文件。
### 移植和包装器
RE2 用 C++ 实现。
官方 Python 包装器[位于 `python` 目录中](https://github.com/google/re2/tree/main/python)
并[在 PyPI 上发布为 `google-re2`](https://pypi.org/project/google-re2/)。
请注意,还有一个 PyPI `re2`,但它不是由 RE2 作者编写的,并且无人维护。请使用 `google-re2`。
还有其他非官方包装器:
- C 包装器位于 。
- D 包装器位于 和 [DUB 上](https://code.dlang.org/packages/re2d)。
- Erlang 包装器位于 和 [Hex 上](https://hex.pm/packages/re2)。
- Inferno 包装器位于 。
- Node.js 包装器位于 和 [NPM 上](https://www.npmjs.com/package/re2)。
- OCaml 包装器位于 和 [OPAM 上](https://opam.ocaml.org/packages/re2/)。
- Perl 包装器位于 和 [CPAN 上](https://metacpan.org/pod/re::engine::RE2)。
- R 包装器位于 和 [CRAN 上](https://cran.r-project.org/web/packages/re2/index.html)。
- Ruby 包装器位于 和 RubyGems (rubygems.org)。
- WebAssembly 包装器位于 和 NPM (npmjs.com)。
[RE2J](https://github.com/google/re2j) 是 RE2 C++ 代码到纯 Java 的移植,
[RE2JS](https://github.com/le0pard/re2js) 是 RE2J 到 JavaScript 的移植。
[Go `regexp` 包](https://go.dev/pkg/regexp)
和 [Rust `regex` crate](https://docs.rs/regex)
不与 RE2 共享代码,但它们遵循相同的原则,
接受相同的语法,并提供相同的效率保证。
### 联系
[问题跟踪器](https://github.com/google/re2/issues)是讨论的最佳场所。
有一个[邮件列表](https://groups.google.com/group/re2-dev)用于了解代码更改。
在发送更改之前,请阅读[贡献指南](https://github.com/google/re2/wiki/Contribute)。
请注意,RE2 不使用 GitHub pull requests。
标签:API密钥检测, Bash脚本, C++, Google开源, RE2, ReDoS防护, 多线程安全, 安全, 拒绝服务攻击防御, 数据擦除, 数据清洗, 文本处理, 无回溯, 模式匹配, 正则表达式库, 线性时间复杂度, 网络安全, 自动化资产收集, 解析器, 超时处理, 输入验证, 逆向工具, 隐私保护