POSIX正则表达式
目录
-
1.1 头文件
-
1.2 注意事项
-
1.3 基本方法
-
1.4 POSIX正则表达式基本规范
-
1.5 源码示例
1 POSIX正则表达式
1.1 头文件
#include <regex.h>
1.2 注意事项
不过在C++的字符串中,'\'表示转义后面的字符,'\\'才表示一个'\',而在正则中,'\'也表示转义,所以一个'正常'的正则在修改为C++正则时,所有的'\'都要加倍。
POSIX正则表达式有自己的一套规范,与常用的正则表达式不完全相同。
1.3 基本方法
1.3.1 regcomp
函数原型
int regcomp(regex_t *preg, const char *regex, int cflags);
功能
编译正则表达式,以便regexec方法使用。
参数含义
preg
preg是一个指向编译后的正则表达式,p意思是pointer,reg意思是regex_t类型。
regex_t是一个结构体数据类型,用来存放编译后的正则表达式,它的成员re_nsub用来存储正则表达式中的子正则表达式的个数,子正则表达式就是用圆括号包起来的部分表达式。
regex
描述正则表达式的字符串。
cflags
决定编译的类型。
REG_EXTENDED
扩展的正则表达式,如果不设置则为基本的正则表达式。
REG_ICASE
不区分大小写。
REG_NOSUB
不用存储匹配后的结果。
如果设置该标志位,那么在regexec将忽略nmatch和pmatch两个参数。
REG_NEWLINE
识别换行符。
‘^’匹配行的开头, 不管regexec中是否设置eflags为REG_NOTBOL。
‘$’匹配行的末尾, 不管regexec中是否设置eflags为REG_NOTEOL。
例如:pattern=”^a$” 被检测字符串为”\na”,那么设置REG_NEWLINE才能匹配成功。
返回值
成功返回0
失败返回错误码
1.3.2 regexec
函数原型
int regexec(const regex_t *preg, const char *string, size_t nmatch,
regmatch_t pmatch[], int eflags);
功能
根据编译好的正则表达式,对字符串进行匹配。
参数含义
preg
经过regcomp编译的正则表达式。
string
待匹配的字符串。
nmatch
匹配到的字符串的个数。
pmatch
匹配到的数组。
regmatch_t 的定义如下
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
rm_so
如果-1==rm_so,表示没有匹配到。
如果-1!=rm_so,表示string中下一个最大子字符串的偏移量(举例string开头的偏移量)。
rm_eo
子字符串的长度。
eflags
REG_NOTBOL
NOTBOL = not-begin-of-line
不匹配行的开头,除非在regcomp编译时cflag设置REG_NEWLINE。
该标志位可能在如下情况使用,传递string的不同部分给regexec,并且string的开头不能被解释成行的开头。
REG_NOTEOL
NOTEOL = not-end-of-line
不匹配行的结束,除非在regcomp编译时cflag设置REG_NEWLINE。
返回值
成功返回0
失败返回REG_NOTMATCH
1.3.3 regerror
函数原型
size_t regerror(int errcode, const regex_t *preg, char *errbuf,
size_t errbuf_size);
功能
将regcomp和regexec返回的errorcode转换成错误信息。
参数含义
errorcode
错误码,由regcomp或regexec获得。
preg
经过regcomp编译的正则表达式。
errbuf
存储错误信息的buffer。
errbuf_size
errbuf的大小。
返回值
返回errbuf存储错误信息所需要的大小。
如果errbuf和errbuf_size都是非0,那么errbuf中有errbuf_size-1大小错误信息字符串,最后是字符串结束标记(‘\0’)。
错误码
regcomp方法返回的错误码
REG_BADBR
BR=back reference
非法使用返回引用操作符
REG_BADPAT
PAT=pattern
非法使用正则表达式操作符,例如group和list
REG_BADRPT
RPT=repetition
非法使用重复的操作符,例如在第一个字符使用 ’*’
REG_EBRACE
EBRACE=error brace
大括号不匹配
REG_EBRACK
EBRACK=error bracket
中括号不匹配
REG_ECOLLATE
ECOLLATE=error collate
非法参数
REG_ECTYPE
ECTYPE=error character type
错误的字符类型
REG_EEND
EEND=error end
无具体错误。该错误码在POSIX.2中没有定义
REG_EESCAPE
EESCAPE=error escape
多余的\
REG_EPAREN
EPAREN=error parenthesis
圆括号不匹配
REG_ERANGE
ERANGE=error range
非法使用范围操作符,例如结束的标识出现在开始标识之前
REG_ESIZE
ESIZE=error size
编译正则表达式需要的内存大于64kb,在POSIX.2中没有定义
REG_ESPACE
ESPACE=error space
编译正则表达式已经超出内存空间
REG_ESUBREG
ESUBREG=error reference to a subexpression
非法使用子表达式的引用
1.3.4 regefree
函数原型
void regfree(regex_t *preg);
功能
释放由regcomp编译preg的内存。
参数含义
preg
经过regcomp编译的正则表达式。
返回值
void
1.4 POSIX正则表达式规范
参考:http://en.wikipedia.org/wiki/Regular_expression
POSIX正则表达式分为Basic Regular Expressions 和 Extended Regular Expressions。
ERE增加支持?,+和|,去除了通配符()和{}。而且POSIX正则表达式的标准语法经常坚持使用附加的语法来支持特殊应用。虽然POSIX.2没有实现一些具体的细节,BRE和ERE提供被很多工具使用的标准。
BRE要求通配符()和{}写成\(\)和\{\},ERE中无需这样。
1.4.1 基本通配符
举例
.at
匹配任何以”at”结尾长度为3的字符串,例如” at”,”aat”,”cat”等
[hc]at
匹配”hat”和”cat”
[^b]at
除了”bat”,匹配任何以”at”结尾长度为3的字符串。例如,”aat”,”cat”等
[^hc]at
除了”hat”和”cat”,匹配任何以”at”结尾长度为3的字符串。例如,”aat”,”tat”等
^[hc]at
匹配任何以”hat”或”cat”开头的字符串或行
[hc]at$
匹配任何以”hat”或”cat”结尾的字符串或行
匹配任何三个字符,第一个和第三个字符必须分别为”[”,”]”,因为”\[”和”\[”是经过转义,例如”[a]”,”[b]”
s.*
匹配任何以”s”开头的字符串,例如”swa”,”seed”
1.4.2 Extended Regular Expressions
在ERE中,反斜杠’\’用来对通配符进行转义,所以BRE中的’\(’和’\)’在ERE中改为’(’和’)’,’\{’和’\}’改为’{’和’}’。 ERE移除了’\n’通配符,并添加了如下通配符。
通配符 |
描述 |
? |
匹配前一个元素0次或1次。例如,ab?c匹配”ac”,”abc”。 |
+ |
匹配前一个元素1次或多次。例如,ab+c匹配”abc”,”abbc”等,但是不能匹配”ac”。 |
| |
匹配前一个表达式或后一个表达式。例如,abc|def匹配”abc”,”def”。 |
举例
[hc]+at
匹配”hat”,”cat”,”hhat”,”ccat”等,但是不匹配”at”
[hc]?at
匹配”hat”,”cat”,”at”
[hc]*at
匹配”at”,”hat”,”cat”,”hcat”等
cat|dog
匹配”cat”,”dog”
1.4.3 Character classes
character class 是除了字面匹配最基本的正则表达式。它是很小的字符序列匹配更大的字符序列。例如,[A-Z]可以表示字母表,\d表示任意数字。character class应用于BRE和ERE。
当使用范围通配符时,例如[a-z]。计算机本地设置决定了字符编码的顺序。计算机可能按a-z的顺序来存储,或者abc…zABC…Z,或者aAbBcC…zZ的顺序。所以POSIX定义了character class,正则表达式的处理器可以正确解析该character class。
POSIX |
ASCII |
描述 |
[:alnum:] |
[A-Za-z0-9] |
数字和字母字符 |
[:alpha:] |
[A-Za-z] |
字母字符 |
[:blank:] |
[ \t] |
空格和TAB |
[:cntrl:] |
[\x00-\x1F\x7F] |
控制符 |
[:digit:] |
[0-9] |
数字 |
[:graph:] |
[\x21-\x7E] |
可视字符 |
[:lower:] |
[a-z] |
小写字母字符 |
[:print:] |
[\x20-\x7E] |
可视字符和空格 |
[:punct:] |
[][!"#$%&'()*+,./:;<=>?@\^_`{|}~-] |
标点符号 |
[:space:] |
[ \t\r\n\v\f] |
空白字符 |
[:upper:] |
[A-Z] |
大写字母字符 |
[:xdigit:] |
[A-Fa-f0-9] |
十六进制字符 |
POSIX定义的character class只能在中括号内使用。例如,[[:upper:]ab]匹配大写字母字符和”a”,”b”。
[:word:]是附加的非POSIX的character class,[:word:]表示[:alnum:]和下划线。这表明在很多编程语言中,这些通配符可能是标识符。
1.5 源码示例
我的github:https://github.com/loverszhaokai/libutil/blob/master/src/regex_util.cc
libutil库中包含有regex的示例。欢迎批评指正,一起修改。
/****************************************************************************** libutil Author: zhaokai Email: loverszhao@gmail.com Reference: POSIX Description: Version: 1.0 ******************************************************************************/ #include "util/regex_util.h" #include <algorithm> #include <climits> #include <iostream> #include <regex.h> namespace util { bool RegexMatch(const std::string &_pattern, const std::string &_input, std::vector<std::string> *_output, std::string *_err_msg) { _output->clear(); *_err_msg = ""; regex_t reg; int comp_result = regcomp(®, _pattern.c_str(), REG_EXTENDED | REG_NEWLINE); if (0 == comp_result) { // Add 1 in case of "" == _input size_t nmatch = 1 + std::min(_input.length(), UINT_MAX - 1); regmatch_t pmatch[nmatch]; int exec_result = regexec(®, _input.c_str(), nmatch, pmatch, 0); if (0 == exec_result) { for (size_t i = 0; i < nmatch; ++i) { if (-1 == pmatch[i].rm_so) break; _output->push_back(_input.substr(pmatch[i].rm_so, pmatch[i].rm_eo)); } } } else { const size_t errbuf_size = 1000; char errbuf[errbuf_size + 1]; regerror(comp_result, ®, errbuf, errbuf_size); *_err_msg = errbuf; regfree(®); return false; } regfree(®); return true; } bool FullMatch(const std::string &_pattern, const std::string &_input) { std::vector<std::string> output; std::string err_msg = ""; bool result = RegexMatch(_pattern, _input, &output, &err_msg); if (!result) { std::cout << err_msg << std::endl; return false; } else { // full-match in case of @_input == output[0] if (0 < output.size() && _input == output[0]) return true; } return false; } }; // namespace util
单元测试文件,包含了Mac,IP,Port,文件夹路径的正则表达式测试
/****************************************************************************** libutil Author: zhaokai Email: loverszhao@gmail.com Reference: chromium Description: Version: 1.0 ******************************************************************************/ #include "util/regex_util.h" #include "util/basictypes.h" #include "third_party/gtest/include/gtest/gtest.h" namespace util { // ------------------------------------------------------------ // --------------------- Update Begin ------------------------- // ------------------------------------------------------------ TEST(RegexUtilTest, RegexMatch) { struct { const std::string pattern; const std::string input; const std::vector<std::string> expect_output; bool result; } cases [] = { { "c", "c", {"c"}, true}, { "c", "", {}, true}, { "(", "", {}, false}, { "0*", "", {""}, true}, { "0*", "0", {"0"}, true}, { "0*", "0000", {"0000"}, true}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { std::string err_msg = ""; std::vector<std::string> output; EXPECT_EQ(cases[i].result, RegexMatch(cases[i].pattern, cases[i].input, &output, &err_msg)) << "cases[" << i << "]"; EXPECT_EQ(cases[i].expect_output, output) << "cases[" << i << "]"; } } TEST(RegexUtilTest, FullMatch) { struct { const std::string pattern; const std::string input; bool result; } cases [] = { { "c", "c", true}, { "c", "", false}, { "(", "", false}, { "^[0-9]*$", "12345", true}, { "^[0-9]*$", "00000", true}, { "^[0-9]*$", "1", true}, { "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", "loverszhao@gmail.com", true}, { "[0-9]{5}", "00000", true}, { "([0-9]){5}", "00000", true}, { "(([0-9]){5}){2}", "0000000000", true}, { "(([0-9]){5}){2}", "0000011111", true}, { "(([0-9]){5}){2}", "0123456789", true}, { "[0-9]", "0", true}, { "0*", "00000", true}, { "0*", "", true}, { "0?", "", true}, { "0?", "0", true}, { "0?", "00", false}, { "[a-z][0-9]{2}{3}", "x000000", true}, { "^a$", "a", true}, { "^a$", "\na", false}, { ".", "a", true}, { ".", "abc", false}, { "\.", ".", true}, { "[.]", "a", false}, { "[:digit:]", "1", false}, { "[[:digit:]]", "1", true}, { "[[:digit:]]{1,2}", "1", true}, { "[[:digit:]]{1,2}", "99", true}, { "^[[:digit:]]{1,2}", "1", true}, { "^[[:digit:]]{1,2}", "99", true}, //"^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\."; { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]])", "99", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]])", "199", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "99", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "199", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "239", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "250", true}, { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.", "250.", true}, { "^([\\/]?[[:alnum:]_]+)*$", "file_name", true}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, FullMatch(cases[i].pattern, cases[i].input)) << "cases[" << i << "] pattern=" << cases[i].pattern << " input=" << cases[i].input << std::endl; } } namespace { // Return true if @_mac is valid, such as "11:11:11:11:11:11" // or "11 11 11 11 11 11" or "111111111111" // flase otherwise bool ValidateMac(const std::string &_mac) { const std::string pattern = "^[0-9a-fA-F]{2}([ -:][0-9a-fA-F]{2}){5}"; return FullMatch(pattern, _mac); } // Return true if @_ip is valid, such as "192.168.4.1" // flase otherwise bool ValidateIP(const std::string &_ip) { const std::string pattern = "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]" "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]" "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]" "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])$"; return FullMatch(pattern, _ip); } // Return true if @_port is valid which should between "0~65535" // and it can be "0" or "65535" // false otherwise bool ValidatePort(const std::string &_port) { const std::string pattern = "^([1-9]|[1-9][[:digit:]]{1,3}|[1-6][0-5][0-5][0-3][0-5])$"; return FullMatch(pattern, _port); } // Return true if @_path is valid, such as "/1/2/3" or "1/2/3/" // false otherwise bool ValidatePath(const std::string &_path) { //const std::string pattern = "^[a-zA-Z]:(((//(?! )[^///:*?\"<>|]+)+//?)|(//))[:space:]*$"; //const std::string pattern = "(^[.]|^/|^[a-zA-Z])?:?/[.]+(/$)? "; //const std::string pattern = "(\/([0-9a-zA-Z]+))+"; const std::string pattern = "^([\\/]?[[:alnum:]_]+)*$"; return FullMatch(pattern, _path); } TEST(RegexUtilTest, ValidateMac) { struct { const std::string input; bool result; } cases [] = { { ":11:22:33:44:55:66", false}, { "11:22:33:44:55:66", true}, { "11-22-33-44-55-66", true}, { "11 22 33 44 55 66", true}, { "112233445566", false}, { "c", false}, { "c", false}, { "(", false}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ValidateMac(cases[i].input)) << "cases[" << i << "] input=" << cases[i].input << std::endl; } } TEST(RegexUtilTest, ValidateIP) { struct { const std::string input; bool result; } cases [] = { // Normal IP { "192.168.4.1", true}, { "10.0.0.1", true}, { "192.168.4.244", true}, // Unnormal IP { "292.168.4.244", false}, { "192.268.-1.244", false}, { "192.168.4.264", false}, { "....", false}, { "192.168.1.", false}, { "192 168 1 1", false}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ValidateIP(cases[i].input)) << "cases[" << i << "] input=" << cases[i].input << std::endl; } } TEST(RegexUtilTest, ValidatePort) { struct { const std::string input; bool result; } cases [] = { // Normal Port { "1", true}, { "99", true}, { "199", true}, { "200", true}, { "999", true}, { "1999", true}, { "30000", true}, { "30001", true}, { "60000", true}, { "65535", true}, // Unnormal Port { "0", false}, { "-1", false}, { "00", false}, { "010", false}, { "65536", false}, { "65537", false}, { "111111", false}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ValidatePort(cases[i].input)) << "cases[" << i << "] input=" << cases[i].input << std::endl; } } TEST(RegexUtilTest, ValidatePath) { struct { const std::string input; bool result; } cases [] = { // Normal Path { "1", true}, { "/1/2/3", true}, { "1/2/3", true}, // Unnormal Path { ".", false}, { "..", false}, { "/1/./", false}, { "/1/../", false}, { "//1/2", false}, { "/1/...", false}, { "/1//", false}, { "./1/./", false}, { "./1/../", false}, { "./1/2/3", false}, { "./1/2/3/", false}, { "1/./", false}, { "1/../", false}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ValidatePath(cases[i].input)) << "cases[" << i << "] input=" << cases[i].input << std::endl; } } } }; // namespace util
作者:loverszhaokai
出处:http://www.cnblogs.com/lovers
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。