Nov
25
使用Linux/Unix/BSD的regex库
C/C++标准里头都没有正则表达式,C++还好,可以用上boost::regex,C的话,最简单的还是用系统自带的正则库了。
这个正则库真是相当简单,如果不关心内部琐碎的细节,实际上它只有2个类型、4个函数和7个常量,详细的后面会列出来(或者直接man regex),这里还是直接看例子比较实在:
代码1:email格式检测
代码2:匹配xml的tag,取出key/value
代码3:找出所有匹配的字符串(regexec只匹配第一个)
==== 分割线 ====
上面给出的几个例子能够满足regex的基本使用了,这里把前面提到的2、4、7给列出来,供参考。
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
这个正则库真是相当简单,如果不关心内部琐碎的细节,实际上它只有2个类型、4个函数和7个常量,详细的后面会列出来(或者直接man regex),这里还是直接看例子比较实在:
代码1:email格式检测
#include <stdio.h>
#include <regex.h>
#include <assert.h>
int main() {
//分配一个regex_t
regex_t reg;
//编译(使用POSIX扩展模式、并忽略大小写),确认编译成功(返回0)
assert(regcomp(®, "^[a-z0-9_]+@([a-z0-9-]+\\.)+[a-z0-9]+$", REG_EXTENDED | REG_ICASE) == 0);
int ret = regexec(®, "steve@rim.jobs", 0, NULL, 0); //执行搜索
//看看返回值:0表示匹配成功,1表示REG_NOMATCH
printf("ret = %d, nomatch = %d\n", ret, REG_NOMATCH);
regfree(®); //记得释放空间
}
#include <regex.h>
#include <assert.h>
int main() {
//分配一个regex_t
regex_t reg;
//编译(使用POSIX扩展模式、并忽略大小写),确认编译成功(返回0)
assert(regcomp(®, "^[a-z0-9_]+@([a-z0-9-]+\\.)+[a-z0-9]+$", REG_EXTENDED | REG_ICASE) == 0);
int ret = regexec(®, "steve@rim.jobs", 0, NULL, 0); //执行搜索
//看看返回值:0表示匹配成功,1表示REG_NOMATCH
printf("ret = %d, nomatch = %d\n", ret, REG_NOMATCH);
regfree(®); //记得释放空间
}
代码2:匹配xml的tag,取出key/value
int main() {
const char *str = "<key>value</key>";
regex_t reg;
assert(regcomp(®, "<([^>]*)>([^<]*)</\\1>", REG_EXTENDED) == 0); //编译
const int nr_match = 3; //串本身 + 2个子匹配
regmatch_t matches[nr_match]; //存储匹配的起始位置和结束位置
int ret = regexec(®, str, nr_match, matches, 0);
if (ret == 0) { //匹配成功
for (int i = 0; i < nr_match; i++) { //先输出整个串,再依次输出子匹配
for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++) // [起始, 结束)
putchar(str[j]);
putchar('\n');
}
}
else if (ret == REG_NOMATCH) { //匹配失败
printf("no match\n");
}
else { //执行错误
char msgbuf[256];
regerror(ret, ®, msgbuf, sizeof(msgbuf)); //输出错误信息至字符数组
printf("error: %s\n", msgbuf);
}
regfree(®);
return 0;
}
//运行结果:
<key>value</key>
key
value
const char *str = "<key>value</key>";
regex_t reg;
assert(regcomp(®, "<([^>]*)>([^<]*)</\\1>", REG_EXTENDED) == 0); //编译
const int nr_match = 3; //串本身 + 2个子匹配
regmatch_t matches[nr_match]; //存储匹配的起始位置和结束位置
int ret = regexec(®, str, nr_match, matches, 0);
if (ret == 0) { //匹配成功
for (int i = 0; i < nr_match; i++) { //先输出整个串,再依次输出子匹配
for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++) // [起始, 结束)
putchar(str[j]);
putchar('\n');
}
}
else if (ret == REG_NOMATCH) { //匹配失败
printf("no match\n");
}
else { //执行错误
char msgbuf[256];
regerror(ret, ®, msgbuf, sizeof(msgbuf)); //输出错误信息至字符数组
printf("error: %s\n", msgbuf);
}
regfree(®);
return 0;
}
//运行结果:
<key>value</key>
key
value
代码3:找出所有匹配的字符串(regexec只匹配第一个)
int main() {
regex_t reg;
const char *str = "<k1>v1</k1><k2>v2</k2><k3>v3</k3>";
assert(regcomp(®, "<([^>]*)>([^<]*)</\\1>", REG_EXTENDED) == 0);
const int nr_match = 3;
regmatch_t matches[nr_match];
const char *start = str;
while (1) {
int ret = regexec(®, start, nr_match, matches, 0);
if (ret == 0)
{
for (int i = 0; i < nr_match; i++)
{
for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++)
putchar(start[j]);
putchar('\n');
}
start += matches[0].rm_eo; //下次从 这次匹配末尾 开始搜索
continue;
}
if (ret == REG_NOMATCH)
printf("no match\n");
else {
char msgbuf[256];
regerror(ret, ®, msgbuf, sizeof(msgbuf));
printf("error: %s\n", msgbuf);
}
break;
}
regfree(®);
return 0;
}
//输出:
<k1>v1</k1>
k1
v1
<k2>v2</k2>
k2
v2
<k3>v3</k3>
k3
v3
no match
regex_t reg;
const char *str = "<k1>v1</k1><k2>v2</k2><k3>v3</k3>";
assert(regcomp(®, "<([^>]*)>([^<]*)</\\1>", REG_EXTENDED) == 0);
const int nr_match = 3;
regmatch_t matches[nr_match];
const char *start = str;
while (1) {
int ret = regexec(®, start, nr_match, matches, 0);
if (ret == 0)
{
for (int i = 0; i < nr_match; i++)
{
for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++)
putchar(start[j]);
putchar('\n');
}
start += matches[0].rm_eo; //下次从 这次匹配末尾 开始搜索
continue;
}
if (ret == REG_NOMATCH)
printf("no match\n");
else {
char msgbuf[256];
regerror(ret, ®, msgbuf, sizeof(msgbuf));
printf("error: %s\n", msgbuf);
}
break;
}
regfree(®);
return 0;
}
//输出:
<k1>v1</k1>
k1
v1
<k2>v2</k2>
k2
v2
<k3>v3</k3>
k3
v3
no match
==== 分割线 ====
上面给出的几个例子能够满足regex的基本使用了,这里把前面提到的2、4、7给列出来,供参考。
//用于储存编译好的正则
typedef struct re_pattern_buffer regex_t ;
//用于存储匹配到的位置(起始、结束偏移量,rm_so是RegexMatch_StartOffset的首字母简写)
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
//将regex正则表达式 编译存储到preg指向的regex_t中去,该regex_t需要事先分配好。编译成功返回0。
int regcomp(regex_t *preg, const char *regex, int cflags);
//其中cflags可以是以下几项的异或
REG_EXTENDED //启用posix扩展模式,(){}+这些字符变成元字符
REG_ICASE //忽略大小写
REG_NOSUB //只匹配,不保存匹配到的正则位置
REG_NEWLINE //启用多行模式:. 不能匹配换行符,且 ^和$可以匹配\n之后的位置和\n之前的位置
//释放编译使额外分配的空间(不会释放regex_t本身)
void regfree(regex_t *preg);
//执行搜索匹配:查找string中满足preg的第一个串,将串的始末位置保存到pmatch[0].rm_so/rm_eo;
//子匹配依次保存到pmatch[1], pmatch[2], ... 调用者要保证匹配的数量不超过nmatch。
//匹配成功返回0,无匹配返回 <REG_NOMATCH> ,其他是出错(比如参数错误)。
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags);
//其中eflags可以是以下的异或
REG_NOTBOL //无视^规则(Beginning Of Line):将字符串分段匹配的时候,后面可能不希望匹配到^
REG_NOTEOL //无视$规则
typedef struct re_pattern_buffer regex_t ;
//用于存储匹配到的位置(起始、结束偏移量,rm_so是RegexMatch_StartOffset的首字母简写)
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
//将regex正则表达式 编译存储到preg指向的regex_t中去,该regex_t需要事先分配好。编译成功返回0。
int regcomp(regex_t *preg, const char *regex, int cflags);
//其中cflags可以是以下几项的异或
REG_EXTENDED //启用posix扩展模式,(){}+这些字符变成元字符
REG_ICASE //忽略大小写
REG_NOSUB //只匹配,不保存匹配到的正则位置
REG_NEWLINE //启用多行模式:. 不能匹配换行符,且 ^和$可以匹配\n之后的位置和\n之前的位置
//释放编译使额外分配的空间(不会释放regex_t本身)
void regfree(regex_t *preg);
//执行搜索匹配:查找string中满足preg的第一个串,将串的始末位置保存到pmatch[0].rm_so/rm_eo;
//子匹配依次保存到pmatch[1], pmatch[2], ... 调用者要保证匹配的数量不超过nmatch。
//匹配成功返回0,无匹配返回 <REG_NOMATCH> ,其他是出错(比如参数错误)。
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags);
//其中eflags可以是以下的异或
REG_NOTBOL //无视^规则(Beginning Of Line):将字符串分段匹配的时候,后面可能不希望匹配到^
REG_NOTEOL //无视$规则
欢迎扫码关注:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。