导读:
GNU/Linux有两套库可用于正则表达式编程:POSIX库和PCRE库。前者不需要单独安装,一般需求还是能满足的,速度稍慢些。后者是久负盛名的Perl正则表达式库,功能强大,匹配速度快,不过可能需要单独安装。
POSIX库
如何使用POSIX库的例子:
#include
#include
#include
char *get_regerror (int errcode, regex_t *compiled)
{
size_t length = regerror (errcode, compiled, NULL, 0);
char *buffer = malloc(length);
if (!buffer) return NULL;
(void) regerror (errcode, compiled, buffer, length);
return buffer;
}
int regtest(const char*pattern, const char* string)
{
regex_t reg;
regmatch_t *subexprs = NULL;
int ret;
int i;
if (0 != (ret=regcomp(?, pattern, REG_EXTENDED))) {
char *buffer = get_regerror(ret, ?);
if (buffer) {
fprintf(stderr, "regcomp:[%d]%s/n", ret, buffer);
free(buffer);
}
return -1;
}
subexprs = malloc((reg.re_nsub+1)*sizeof(regmatch_t));
if (!subexprs) {
fprintf(stderr, "error malloc subexprs/n");
regfree(?);
return -1;
}
if (0 != (ret=regexec(?, string, reg.re_nsub+1, subexprs, 0))) {
char *buffer = get_regerror(ret, ?);
if (buffer) {
fprintf(stderr, "regexec:[%d]%s/n", ret, buffer);
free(buffer);
}
regfree(?);
return -1;
}
for (i = 0; i <= reg.re_nsub; i++) {
printf("[%d]:", i);
if (subexprs[i].rm_so == subexprs[i].rm_eo) {
printf("[EMPTY SUBEXPR]/n");
}
else if (subexprs[i].rm_so == -1 ||
subexprs[i].rm_eo == -1) {
printf("[NO SUBEXPR]/n");
}
else {
fwrite(string+subexprs[i].rm_so, 1,
subexprs[i].rm_eo-subexprs[i].rm_so, stdout);
printf("/n");
}
}
regfree(?);
if (subexprs) free(subexprs);
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: regtest pattern string/n");
return -1;
}
fprintf(stderr, "pattern:%s/n", argv[1]);
fprintf(stderr, "string:%s/n", argv[2]);
return regtest(argv[1], argv[2]);
}
在字符串匹配之前,必须先 编译匹配模式,这是通过regcomp实现的。这个函数的原型如下:
int regcomp (regex_t *compiled, const char *pattern, int cflags)
参数compiled有一个成员需要关注:re_nsub,代表编译后的子表达式数目,由于需要保存整个匹配到的模式,所以最终匹配的条目数是re_nsub加1。cflags用来修饰匹配模式,可取值如下:
REG_EXTENDED 启用POSIX正则库扩展,关于该扩展的详细信息可参考POSIX规范
REG_ICASE 忽略大小写
REG_NOSUB 不要存储子表达式
REG_NEWLINE 把换行符作为多行的分隔符,这样'$'可匹配每一行的行尾,'^'匹配每一行的行首,'.'不匹配换行符,[^...]不匹配新行
编译完模式后从内存中分配子表达式存储空间,然后调用regexec对串进行匹配,该函数原型如下:
int regexec (regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr [], int eflags)
nmatch指明matchptr数组的数目,该数目是compiled->re_nsub+1,也可以让nmatch为0,matchptr为NULL,表示不要保存子表达式。eflags通常为0。
匹配结束后,匹配到的子表达式在串中的偏移保存在regmatch_t结构中,该结构有两个成员:
rm_so 子表达式的起始偏移
rm_eo 子表达式的结束偏移
这是一个开区间,实际的子表达式在[rm_so,rm_eo)里。
如果没有匹配的子表达式,比如"f(o*)"匹配"fum",实际匹配到的只有"f",这时rm_so和rm_eo相等,都为1。如果整个模式在没有子表示式的情况下也能匹配,这时rm_so和rm_eo为-1,比如"ba(na)*"匹配"ba"。
PCRE库
PCRE库的功能虽然强大,可是并不难使用。详细信息可参考http://www.pcre.org/里的文档,附录有一个例子,比较全面地阐述了如何调用该库。
PCRE的子表达式和POSIX类似,不过它还引入了一个 命名子表达式的概念。比如模式"(?P(?P(/d/d)?/d/d)-(?P/d/d)-(?P/d/d))",其中的date, year, month, day是对子表达式的命名。如何根据这些名称来获取子表达式,PCRE文档有详细的说明。
附录:pcredemo.c
/*************************************************
* PCRE DEMONSTRATION PROGRAM *
*************************************************/
/* This is a demonstration program to illustrate the most straightforward ways
of calling the PCRE regular expression library from a C program. See the
pcresample documentation for a short discussion.
Compile thuswise:
gcc -Wall pcredemo.c -I/usr/local/include -L/usr/local/lib /
-R/usr/local/lib -lpcre
Replace "/usr/local/include" and "/usr/local/lib" with wherever the include and
library files for PCRE are installed on your system. Only some operating
systems (e.g. Solaris) use the -R option.
*/
#include
#include
#include
#define OVECCOUNT 30 /* should be a multiple of 3 */
int main(int argc, char **argv)
{
pcre *re;
const char *error;
char *pattern;
char *subject;
unsigned char *name_table;
int erroffset;
int find_all;
int namecount;
int name_entry_size;
int ovector[OVECCOUNT];
int subject_length;
int rc, i;
/**************************************************************************
* First, sort out the command line. There is only one possible option at *
* the moment, "-g" to request repeated matching to find all occurrences, *
* like Perl's /g option. We set the variable find_all to a non-zero value *
* if the -g option is present. Apart from that, there must be exactly two *
* arguments. *
**************************************************************************/
find_all = 0;
for (i = 1; i
GNU/Linux有两套库可用于正则表达式编程:POSIX库和PCRE库。前者不需要单独安装,一般需求还是能满足的,速度稍慢些。后者是久负盛名的Perl正则表达式库,功能强大,匹配速度快,不过可能需要单独安装。
POSIX库
如何使用POSIX库的例子:
#include
#include
#include
char *get_regerror (int errcode, regex_t *compiled)
{
size_t length = regerror (errcode, compiled, NULL, 0);
char *buffer = malloc(length);
if (!buffer) return NULL;
(void) regerror (errcode, compiled, buffer, length);
return buffer;
}
int regtest(const char*pattern, const char* string)
{
regex_t reg;
regmatch_t *subexprs = NULL;
int ret;
int i;
if (0 != (ret=regcomp(?, pattern, REG_EXTENDED))) {
char *buffer = get_regerror(ret, ?);
if (buffer) {
fprintf(stderr, "regcomp:[%d]%s/n", ret, buffer);
free(buffer);
}
return -1;
}
subexprs = malloc((reg.re_nsub+1)*sizeof(regmatch_t));
if (!subexprs) {
fprintf(stderr, "error malloc subexprs/n");
regfree(?);
return -1;
}
if (0 != (ret=regexec(?, string, reg.re_nsub+1, subexprs, 0))) {
char *buffer = get_regerror(ret, ?);
if (buffer) {
fprintf(stderr, "regexec:[%d]%s/n", ret, buffer);
free(buffer);
}
regfree(?);
return -1;
}
for (i = 0; i <= reg.re_nsub; i++) {
printf("[%d]:", i);
if (subexprs[i].rm_so == subexprs[i].rm_eo) {
printf("[EMPTY SUBEXPR]/n");
}
else if (subexprs[i].rm_so == -1 ||
subexprs[i].rm_eo == -1) {
printf("[NO SUBEXPR]/n");
}
else {
fwrite(string+subexprs[i].rm_so, 1,
subexprs[i].rm_eo-subexprs[i].rm_so, stdout);
printf("/n");
}
}
regfree(?);
if (subexprs) free(subexprs);
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: regtest pattern string/n");
return -1;
}
fprintf(stderr, "pattern:%s/n", argv[1]);
fprintf(stderr, "string:%s/n", argv[2]);
return regtest(argv[1], argv[2]);
}
在字符串匹配之前,必须先 编译匹配模式,这是通过regcomp实现的。这个函数的原型如下:
int regcomp (regex_t *compiled, const char *pattern, int cflags)
参数compiled有一个成员需要关注:re_nsub,代表编译后的子表达式数目,由于需要保存整个匹配到的模式,所以最终匹配的条目数是re_nsub加1。cflags用来修饰匹配模式,可取值如下:
REG_EXTENDED 启用POSIX正则库扩展,关于该扩展的详细信息可参考POSIX规范
REG_ICASE 忽略大小写
REG_NOSUB 不要存储子表达式
REG_NEWLINE 把换行符作为多行的分隔符,这样'$'可匹配每一行的行尾,'^'匹配每一行的行首,'.'不匹配换行符,[^...]不匹配新行
编译完模式后从内存中分配子表达式存储空间,然后调用regexec对串进行匹配,该函数原型如下:
int regexec (regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr [], int eflags)
nmatch指明matchptr数组的数目,该数目是compiled->re_nsub+1,也可以让nmatch为0,matchptr为NULL,表示不要保存子表达式。eflags通常为0。
匹配结束后,匹配到的子表达式在串中的偏移保存在regmatch_t结构中,该结构有两个成员:
rm_so 子表达式的起始偏移
rm_eo 子表达式的结束偏移
这是一个开区间,实际的子表达式在[rm_so,rm_eo)里。
如果没有匹配的子表达式,比如"f(o*)"匹配"fum",实际匹配到的只有"f",这时rm_so和rm_eo相等,都为1。如果整个模式在没有子表示式的情况下也能匹配,这时rm_so和rm_eo为-1,比如"ba(na)*"匹配"ba"。
PCRE库
PCRE库的功能虽然强大,可是并不难使用。详细信息可参考http://www.pcre.org/里的文档,附录有一个例子,比较全面地阐述了如何调用该库。
PCRE的子表达式和POSIX类似,不过它还引入了一个 命名子表达式的概念。比如模式"(?P(?P(/d/d)?/d/d)-(?P/d/d)-(?P/d/d))",其中的date, year, month, day是对子表达式的命名。如何根据这些名称来获取子表达式,PCRE文档有详细的说明。
附录:pcredemo.c
/*************************************************
* PCRE DEMONSTRATION PROGRAM *
*************************************************/
/* This is a demonstration program to illustrate the most straightforward ways
of calling the PCRE regular expression library from a C program. See the
pcresample documentation for a short discussion.
Compile thuswise:
gcc -Wall pcredemo.c -I/usr/local/include -L/usr/local/lib /
-R/usr/local/lib -lpcre
Replace "/usr/local/include" and "/usr/local/lib" with wherever the include and
library files for PCRE are installed on your system. Only some operating
systems (e.g. Solaris) use the -R option.
*/
#include
#include
#include
#define OVECCOUNT 30 /* should be a multiple of 3 */
int main(int argc, char **argv)
{
pcre *re;
const char *error;
char *pattern;
char *subject;
unsigned char *name_table;
int erroffset;
int find_all;
int namecount;
int name_entry_size;
int ovector[OVECCOUNT];
int subject_length;
int rc, i;
/**************************************************************************
* First, sort out the command line. There is only one possible option at *
* the moment, "-g" to request repeated matching to find all occurrences, *
* like Perl's /g option. We set the variable find_all to a non-zero value *
* if the -g option is present. Apart from that, there must be exactly two *
* arguments. *
**************************************************************************/
find_all = 0;
for (i = 1; i