编译时正则表达式 v3
快速的编译时正则表达式,支持在编译时或运行时进行匹配/搜索/捕获。
你可以使用 single-header
目录中的单头文件版本。可以通过 make single-header
重新生成该头文件。如果你使用 cmake,可以将此目录作为子目录添加,并链接到目标 ctre
。
更多信息请访问 compile-time.re
该库可以做什么
ctre::match<"REGEX">(subject); // C++20
"REGEX"_ctre.match(subject); // C++17 + N3599 扩展
- 匹配
- 搜索 (
search
或starts_with
) - 捕获内容 (也支持命名捕获)
- 反向引用 (\g{N} 语法,以及 \1...\9 语法)
- 多行支持 (使用
multi_
函数) - Unicode 属性和 UTF-8 支持
该库实现了大部分 PCRE 语法,但有以下几个例外:
- 回调
- 注释
- 条件模式
- 控制字符 (
\cX
) - 匹配点重置 (
\K
) - 命名字符
- 八进制数
- 选项 / 模式
- 子例程
- unicode 字符簇 (
\X
)
更多文档请访问 pcre.org。
未知字符转义行为
并非所有转义字符都会自动插入自身,库的行为是有特殊含义的转义字符,未知的转义字符会导致语法错误。
明确允许仅插入字符本身的字符转义有:
\-\"\<\>
基本 API
这是从用户角度近似的 API 规范(省略了无处不在的 constexpr
和 noexcept
,并使用 C++20 语法,尽管 API 与 C++17 兼容):
// 检查整个输入是否匹配正则表达式:
template <fixed_string regex> auto ctre::match(auto Range &&) -> regex_results;
template <fixed_string regex> auto ctre::match(auto First &&, auto Last &&) -> regex_results;
// 查找输入中是否包含匹配:
template <fixed_string regex> auto ctre::search(auto Range &&) -> regex_results;
template <fixed_string regex> auto ctre::search(auto First &&, auto Last &&) -> regex_results;
// 检查输入是否以匹配开始(但不需要匹配全部):
template <fixed_string regex> auto ctre::starts_with(auto Range &&) -> regex_results;
template <fixed_string regex> auto ctre::starts_with(auto First &&, auto Last &&) -> regex_results;
// 结果类型可以解构为结构化绑定
template <...> struct regex_results {
operator bool() const; // 是否匹配
auto to_view() const -> std::string_view; // 也可用 view()
auto to_string() const -> std::string; // 也可用 str()
operator std::string_view() const; // 也支持所有字符变体
explicit operator std::string() const;
// 也有 size(), begin(), end(), data()
size_t count() const; // 捕获数量
template <size_t Id> const captured_content & get() const; // 提供特定捕获,整个 regex_results 是隐式捕获 0
};
范围输出 API
// 在输入中搜索正则表达式并返回每个出现,忽略其余部分:
template <fixed_string regex> auto ctre::range(auto Range &&) -> range of regex_result;
template <fixed_string regex> auto ctre::range(auto First &&, auto Last &&) -> range of regex_result;
// 返回每个匹配的范围,在无法匹配的地方停止
template <fixed_string regex> auto ctre::tokenize(auto Range &&) -> range of regex_result;
template <fixed_string regex> auto ctre::tokenize(auto First &&, auto Last &&) -> range of regex_result;
// 返回由正则表达式分割的输入部分,将其作为隐式零捕获的内容部分返回(其他捕获不变,你可以使用它来访问值的分割方式):
template <fixed_string regex> auto ctre::split(auto Range &&) -> regex_result;
template <fixed_string regex> auto ctre::split(auto First &&, auto Last &&) -> range of regex_result;
函数对象
所有函数(ctre::match
, ctre::search
, ctre::starts_with
, ctre::range
, ctre::tokenize
, ctre::split
)都是函数对象,可以不使用括号:
auto matcher = ctre::match<"regex">;
if (matcher(input)) ...
可能的主体(输入)
std::string
类对象(std::string_view
或你自己的字符串,如果它提供具有前向迭代器的begin
/end
函数)- 前向迭代器对
Unicode 支持
要启用,你需要包含:
<ctre-unicode.hpp>
- 或
<ctre.hpp>
和<unicode-db.hpp>
否则,如果你试图在不启用的情况下使用 unicode 支持,将会得到缺失符号。
支持的编译器
- clang 7.0+ (模板 UDL, C++17 语法)
- xcode clang 10.0+ (模板 UDL, C++17 语法)
- clang 12.0+ (C++17 语法, C++20 cNTTP 语法)
- gcc 8.0+ (模板 UDL, C++17 语法)
- gcc 9.0+ (C++17 & C++20 cNTTP 语法)
- MSVC 14.29+ (Visual Studio 16.11+) (C++20)
模板 UDL 语法
编译器必须支持 N3599 扩展,例如 gcc (GCC 9.1+ 除外)和 clang 中的 GNU 扩展。
constexpr auto match(std::string_view sv) noexcept {
using namespace ctre::literals;
return "h.*"_ctre.match(sv);
}
如果你在 GCC 9.1+ 中需要 N3599 扩展,则不能使用 -pedantic。此外,你需要定义宏 CTRE_ENABLE_LITERALS
。
C++17 语法
你可以提供一个 constexpr ctll::fixed_string
变量作为模式。
static constexpr auto pattern = ctll::fixed_string{ "h.*" };
constexpr auto match(std::string_view sv) noexcept {
return ctre::match<pattern>(sv);
}
(这在 MSVC 15.8.8 中测试通过)
C++20 语法
目前,唯一支持 cNTTP 语法 ctre::match<PATTERN>(subject)
的编译器是 GCC 9+。
constexpr auto match(std::string_view sv) noexcept {
return ctre::match<"h.*">(sv);
}
示例
从输入中提取数字
std::optional<std::string_view> extract_number(std::string_view s) noexcept {
if (auto m = ctre::match<"[a-z]+([0-9]+)">(s)) {
return m.get<1>().to_view();
} else {
return std::nullopt;
}
}
从日期中提取值
struct date { std::string_view year; std::string_view month; std::string_view day; };
std::optional<date> extract_date(std::string_view s) noexcept {
using namespace ctre::literals;
if (auto [whole, year, month, day] = ctre::match<"(\\d{4})/(\\d{1,2})/(\\d{1,2})">(s); whole) {
return date{year, month, day};
} else {
return std::nullopt;
}
}
//static_assert(extract_date("2018/08/27"sv).has_value());
//static_assert((*extract_date("2018/08/27"sv)).year == "2018"sv);
//static_assert((*extract_date("2018/08/27"sv)).month == "08"sv);
//static_assert((*extract_date("2018/08/27"sv)).day == "27"sv);
使用捕获
auto result = ctre::match<"(?<year>\\d{4})/(?<month>\\d{1,2})/(?<day>\\d{1,2})">(s);
return date{result.get<"year">(), result.get<"month">, result.get<"day">};
// 或者在 C++ 模拟中,但对象必须有链接
static constexpr ctll::fixed_string year = "year";
static constexpr ctll::fixed_string month = "month";
static constexpr ctll::fixed_string day = "day";
return date{result.get<year>(), result.get<month>, result.get<day>};
// 或使用编号访问
// 捕获 0 是整个匹配
return date{result.get<1>(), result.get<2>, result.get<3>};
词法分析器
enum class type {
unknown, identifier, number
};
struct lex_item {
type t;
std::string_view c;
};
std::optional<lex_item> lexer(std::string_view v) noexcept {
if (auto [m,id,num] = ctre::match<"([a-z]+)|([0-9]+)">(v); m) {
if (id) {
return lex_item{type::identifier, id};
} else if (num) {
return lex_item{type::number, num};
}
}
return std::nullopt;
}
输入范围
这是初步支持,API 可能会发生变化。
auto input = "123,456,768"sv;
for (auto match: ctre::range<"([0-9]+),?">(input)) {
std::cout << std::string_view{match.get<0>()} << "\n";
}
Unicode
#include <ctre-unicode.hpp>
#include <iostream>
// 如果要输出到终端,需要以下内容
std::string_view cast_from_unicode(std::u8string_view input) noexcept {
return std::string_view(reinterpret_cast<const char *>(input.data()), input.size());
}
int main()
{
using namespace std::literals;
std::u8string_view original = u8"Tu es un génie"sv;
for (auto match : ctre::range<"\\p{Letter}+">(original))
std::cout << cast_from_unicode(match) << std::endl;
return 0;
}
使用 vcpkg 安装 ctre
你可以使用 vcpkg 依赖管理器下载和安装 ctre:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install ctre
vcpkg 中的 ctre 端口由 Microsoft 团队成员和社区贡献者保持更新。如果版本过时,请在 vcpkg 仓库上创建一个 issue 或 pull request。
运行测试(适用于开发者)
只需在项目根目录运行 make
。