目录
1 为什么需要正则表达式
2 正则表达式简介
3 正则表达式规则
4 regcomp、regexec、regfree函数
4.1 函数介绍
4.2 URL格式案例
1 为什么需要正则表达式
在许多的应用程序当中, 有这样的应用场景: 给定一个字符串,检查该字符串是否符合某种条件或规则、或者从给定的字符串中找出符合某种条件或规则的子字符串, 将匹配到的字符串提取出来。这种需要在很多的应用程序当中是存在的:
- 例如,很多应用程序都有这种校验功能,譬如检验用户输入的账号或密码是否符合它们定义的规则,如果不符合规则通常会提示用户按照正确的规则输入用户名或密码。
- 譬如给定一个字符串,在程序当中判断该字符串是否是一个 IP 地址, 对于实现这个功能, 大家可能首先想到的是,使用万能的 for 循环, 当然,笔者首先肯定的是, 使用 for 循环自然是可以解决这个问题, 但是在程序代码处理上会比较麻烦,有兴趣的朋友可以自己试一下。
对于这些需求,其实只需要通过一个正则表达式就可以搞定了, 下一小节开始将向大家介绍正则表达式。
2 正则表达式简介
正则表达式又称为规则表达式(Regular Expression),正则表达式通常被用来检索、替换那些符合某个模式(规则)的字符串,正则表达式描述了一种字符串的匹配模式(pattern),可以用来检查一个给定的字符串中是否含有某种子字符串、将匹配的字符串替换或者从某个字符串中取出符合某个条件的子字符串。
在 Linux 系统下运行命令的时候,使用过?或*通配符来查找硬盘上的文件或者文本中的某个字符串, ?通配符匹配 0 个或 1 个字符,而*通配符匹配 0 个或多个字符,譬如"data?.txt"这样的匹配模式可以将下列文件查找出来:
data.dat
data1.dat
data2.dat
datax.dat
dataN.dat
许多程序设计语言都支持正则表达式。譬如,在 Perl 中就内建了一个功能强大的正则表达式引擎、Python提供了内置模块 re 用于处理正则表达式, 正则表达式这个概念最初是由 Unix 中的工具软件(例如 sed 和grep)普及开的。同样,在 C 语言函数库中也提供了用于处理正则表达式的接口供程序员使用。
3 正则表达式规则
正则表达式的匹配语法和规则是用于指定搜索模式的字符串,它们由普通字符(如字母和数字)和特殊字符组成。特殊字符有着不同的用途,例如匹配模式、重复次数、字符类等。下面是一些常用的正则表达式元素及其匹配规则:
普通字符:直接匹配自身,如 a
匹配字符 'a'。
特殊字符:
.
:匹配任意单个字符(除了换行符)。^
:匹配输入字符串的开始位置。$
:匹配输入字符串的结束位置。|
:逻辑或操作符,匹配两个模式中的一个。[]
:字符集,匹配括号内的任意字符,如[abc]
匹配 'a'、'b' 或 'c'。()
:分组,将多个规则组合成一个单元,也可以用于捕获匹配的子字符串。
量词:指定元素出现的次数。
*
:匹配前面的子模式零次或多次。+
:匹配前面的子模式一次或多次。?
:匹配前面的子模式零次或一次。{n}
:匹配确定的 n 次数。{n,}
:至少匹配 n 次。{n,m}
:最少匹配 n 次且最多 m 次。
转义特殊字符:使用 \
对特殊字符进行转义,使其作为普通字符匹配,如 \.
匹配字符 '.'。
字符类:
\d
:匹配任何数字,等同于[0-9]
。\w
:匹配任何字母数字字符,包括下划线,等同于[A-Za-z0-9_]
。\s
:匹配任何空白字符(空格、制表符、换行符等)。
否定字符集:使用 ^
放在 [
后面来表示否定字符集,如 [^abc]
匹配除了 'a'、'b'、'c' 之外的任何字符。
懒惰(非贪婪)匹配:在量词后面加上 ?
使其变为懒惰模式,尽可能少地匹配字符,如 .*?
尽可能少地匹配任何字符。
断言:
\b
:匹配单词边界。(?=...)
:正向先行断言,匹配...前面的位置。(?!...)
:负向先行断言,匹配除了...之外的前面的位置。
回溯引用:使用 \1
、\2
、... 来引用之前通过分组捕获的匹配。
模式修饰符:
i
:不区分大小写。g
:全局搜索。m
:多行匹配。
4 regcomp、regexec、regfree函数
regcomp
、regexec
和 regfree
是正则表达式库中的三个核心函数,用于编译、匹配、释放正则表达式。
4.1 函数介绍
regcomp() 函数:编译正则表达式字符串,生成一个正则表达式对象。原型:
#include <regex.h>
int regcomp(regex_t *preg, const char *pattern, int cflags);
preg
:指向regex_t
结构的指针,用于存储编译后的正则表达式。pattern
:要编译的正则表达式字符串。cflags
:编译时的选项标志,常用的有REG_EXTENDED
(扩展正则表达式)和REG_ICASE
(不区分大小写的匹配)。
regexec() 函数:使用编译后的正则表达式对象匹配目标字符串。
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
preg
:指向编译后的正则表达式的regex_t
结构。string
:要匹配的字符串。nmatch
:匹配结果数组的大小,0 表示不存储匹配结果。pmatch
:指向regmatch_t
数组的指针,用于存储每个匹配结果的起始和结束位置。数组的大小至少为nmatch
。eflags
:执行匹配时的选项标志,如REG_NOTBOL
(^
不匹配字符串的开始)和REG_NOTEOL
($
不匹配字符串的结束)。
regfree() 函数:释放 regcomp()
函数分配的内存资源。
void regfree(regex_t *preg);
preg
:指向regex_t
结构的指针,该结构是之前通过regcomp()
函数编译得到的。
4.2 URL格式案例
^((ht|f)tps?)://[-A-Za-z0-9_]+(\.[-A-Za-z0-9_]+)+([-A-Za-z0-9_.,@?^=%&:/~+#]*[-A-Za-z0-9_@?^=%&/~+#])?$
上面的正则表达式用于匹配大多数的URL格式。下面是对正则表达式各部分的解释:
^
:匹配字符串的开始。((ht|f)tps?)
:匹配 "http" 或 "https",问号表示前面的字符 "s" 是可选的。://
:匹配 "://",这是URL协议部分和域名部分的分隔符。[-A-Za-z0-9_]+
:匹配域名的第一个部分,允许字母、数字、连字符和下划线,+
表示一个或多个。(\.[-A-Za-z0-9_]+)+
:匹配域名的后续部分,可以有多个,格式为点后跟一个或多个允许的字符。([-A-Za-z0-9_.,@?^=%&:/~+#]*
:匹配URL的路径部分的开始,允许字母、数字、点、逗号和其他一些URL中常见的特殊字符,*
表示零个或多个。[-A-Za-z0-9_@?^=%&/~+#])
:正则表达式的最后部分,匹配路径的结束,允许的字符与上面相同,但这里只允许一个字符,因为整个路径部分已经通过前面的*
匹配了任意长度。?
:使路径部分成为可选。$
:匹配字符串的结束。
这个正则表达式可以匹配以下类型的URL:
http://example.com
https://www.example.com
http://example.com/path/to/resource
https://example.com?query=string
下面的程序,使用正则表达式库来验证用户输入的URL字符串是否符合特定的格式。
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <string.h> // 引入字符串处理函数
#define MAX_URL_LENGTH 1024
int main() {
char url[MAX_URL_LENGTH];
// 修正后的正则表达式,注意转义序列
const char *pattern =
"^((ht|f)tps?)://[-A-Za-z0-9_]+(\\.[-A-Za-z0-9_]+)+([-A-Za-z0-9_.,@?^=%&:/~+#]*[-A-Za-z0-9_@?^=%&/~+#])?$";
regex_t regex;
int ret;
// 请求用户输入URL
printf("请输入URL: ");
fgets(url, MAX_URL_LENGTH, stdin); // 使用fgets读取一行输入
// 去除fgets读取的末尾换行符
size_t len = strlen(url);
if(len > 0 && url[len - 1] == '\n') {
url[len - 1] = '\0';
}
// 编译正则表达式
ret = regcomp(®ex, pattern, REG_EXTENDED);
if (ret) {
fprintf(stderr, "Error compiling regex\n");
exit(1);
}
// 执行匹配
ret = regexec(®ex, url, 0, NULL, 0);
if (!ret) {
printf("'%s' 是一个有效的URL。\n", url);
} else {
printf("'%s' 不是一个有效的URL。\n", url);
}
// 释放正则表达式分配的资源
regfree(®ex);
return 0;
}
首先程序提示用户输入URL,使用fgets
函数读取用户输入后,程序去除末尾的换行符。接着、编译一个预定义的正则表达式,并用它来检查输入的URL。如果URL匹配正则表达式模式,则认为它是有效的,并打印相应消息;否则,打印无效消息。最后,程序释放与正则表达式相关的资源。程序运行结果如下: