在C++中使用PCRE正则无法匹配完整字符串的问题
问题:PCRE2匹配Unicode双字节字符时拆分匹配的原因
正则表达式 '(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+ 可完整匹配字符串 Ġmeousrtr,但在C++中使用PCRE2库时,却得到3个独立匹配结果而非1个完整匹配。已知Ġ是双字节Unicode字符,为何会出现这种差异?
C++中的匹配输出
# 正则表达式输出 '(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+ # 匹配结果 Match Succeeded at 0 �x Match Succeeded at 1 �x Match Succeeded at 2 meousrtrx
对应的C++代码
#define PCRE2_CODE_UNIT_WIDTH 8 #include <pcre2.h> #include <string.h> #include <iostream> using namespace std; int main(int argc, char **argv) { PCRE2_SPTR expression = (PCRE2_SPTR) "'(?:[sdmt]|ll|ve|re)| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+"; PCRE2_SPTR text = (PCRE2_SPTR) "Ġmeousrtr"; PCRE2_SIZE eoffset; PCRE2_SIZE *ovector; pcre2_code *re; pcre2_match_data *match_data; char *c = (char *)expression; while (*c) printf("%c", (unsigned int)*c++); printf("\n"); int error_number; int result; size_t start_offset = 0; size_t text_len; u_int32_t options = 0; text_len = strlen((char *)text); re = pcre2_compile(expression, PCRE2_ZERO_TERMINATED, 0, &error_number, &eoffset, NULL); if (re == NULL) { PCRE2_UCHAR buffer[256]; pcre2_get_error_message(error_number, buffer, sizeof(buffer)); cout << buffer; return 1; } match_data = pcre2_match_data_create_from_pattern(re, NULL); while (true) { result = pcre2_match(re, text, text_len, start_offset, options, match_data, NULL); if (result < 0) { switch (result) { case PCRE2_ERROR_NOMATCH: cout << "No matches found!"; return 0; default: cout << "Matching Error" << result; return -1; } pcre2_match_data_free(match_data); pcre2_code_free(re); } ovector = pcre2_get_ovector_pointer(match_data); printf("Match Succeeded at %d\n", ovector[0]); int i; for (i = 0; i < result; i++) { PCRE2_SPTR substring_start = text + ovector[2 * i]; PCRE2_SIZE substring_length = ovector[2 * i + 1] - ovector[2 * i]; printf("%.*s\n", (int)substring_length, (char *)substring_start); } start_offset = ovector[1]; } }
原因及解决方法
核心问题
问题根源在于PCRE2默认未启用Unicode模式,同时代码用单字节(8位)模式处理多字节Unicode字符:
PCRE2_CODE_UNIT_WIDTH 8指定PCRE2将输入视为单字节序列,而Ġ是UTF-8编码的双字节字符(0xC4 0xA0),会被拆成两个独立单字节处理。- 非Unicode模式下,
\p{L}只能识别ASCII字母,无法匹配双字节的Ġ,这两个字节会被正则的?[^\s\p{L}\p{N}]+分支匹配;后面的meousrtr则匹配?\p{L}+分支,最终导致三次拆分匹配。
解决步骤
要让PCRE2和regex101行为一致,需做以下修改:
- 启用Unicode模式:编译正则时添加
PCRE2_UTF选项,让PCRE2以UTF-8编码解析输入字符串。 - 确保字符串编码正确:源码文件需保存为UTF-8编码,编译器需支持UTF-8字符串字面量(如GCC可添加
-finput-charset=UTF-8 -fexec-charset=UTF-8编译选项)。 - 修正长度计算:移除
strlen,直接用PCRE2_ZERO_TERMINATED让PCRE2自动计算字符串长度。
修改后的关键代码片段
// 启用Unicode模式 u_int32_t options = PCRE2_UTF; // 编译正则时传递Unicode选项 re = pcre2_compile(expression, PCRE2_ZERO_TERMINATED, options, &error_number, &eoffset, NULL); // 匹配时使用PCRE2_ZERO_TERMINATED自动处理长度 result = pcre2_match(re, text, PCRE2_ZERO_TERMINATED, start_offset, options, match_data, NULL);
内容的提问来源于stack exchange,提问作者akhildevelops
相关产品推荐
相关产品推荐

