一、正则表达式基础
1.1 什么是正则表达式
正则表达式,说白了就是一套用来描述文本模式的规则。你可以把它想象成一种「超级通配符」——比 *.txt 这种简单匹配强大得多。
我刚开始接触正则时,觉得它像天书。一堆 ^、$、\d 符号,看着就头疼。但用顺手之后,你会发现它简直是文本处理的瑞士军刀。
举个例子:你想从一段日志里提取所有 IP 地址。用普通字符串函数写,得循环、判断、截取,少说十几行代码。用正则表达式,一行模式串就搞定:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}。
核心定义:正则表达式(Regular Expression,简称 regex)是一种用于匹配字符串中字符组合的模式。它由普通字符(如 a-z)和特殊字符(元字符)组成。
1.2 正则表达式在C语言中的应用场景
很多初学者会问:C 语言里用正则,是不是有点大材小用?其实恰恰相反。C 语言做底层开发、嵌入式、网络编程时,正则表达式特别实用。我列几个我实际遇到过的场景:
- 日志解析:我在做网络设备驱动时,需要从上千行调试日志里提取错误码和时间戳。用正则一行模式串搞定,比手写状态机快多了。
- 配置文件读取:很多嵌入式系统的配置文件是文本格式的。用正则校验 key=value 的格式,比逐个字符解析靠谱。
- 输入验证:比如检查用户输入的邮箱、电话号码是否合法。正则表达式可以一次性完成格式校验。
- 协议解析:处理简单的文本协议(如 HTTP 头)时,正则表达式能快速提取字段。
我的经验:正则表达式在 C 语言中不是万能的。对于复杂的嵌套结构(比如 JSON 解析),还是得用专门的解析器。正则擅长的是「有规律但不嵌套」的文本模式。
1.3 常用正则表达式语法速览
这里我整理了一份最常用的语法表。你不需要一次性全记住,先有个印象,后面用到时再回来查。
| 元字符 | 含义 | 示例 |
|---|---|---|
. |
匹配任意单个字符(除换行符) | a.b 匹配 "acb"、"a1b" |
* |
匹配前一个字符 0 次或多次 | ab*c 匹配 "ac"、"abc"、"abbc" |
+ |
匹配前一个字符 1 次或多次 | ab+c 匹配 "abc"、"abbc",不匹配 "ac" |
? |
匹配前一个字符 0 次或 1 次 | ab?c 匹配 "ac"、"abc" |
^ |
匹配字符串开头 | ^abc 匹配以 "abc" 开头的字符串 |
$ |
匹配字符串结尾 | abc$ 匹配以 "abc" 结尾的字符串 |
\d |
匹配一个数字(等价于 [0-9]) | \d{3} 匹配三位数字 |
\w |
匹配一个字母、数字或下划线 | \w+ 匹配一个单词 |
\s |
匹配一个空白字符(空格、制表符等) | \s+ 匹配连续空白 |
[abc] |
字符集,匹配括号内任意一个字符 | [aeiou] 匹配任意元音字母 |
(pattern) |
捕获分组,将匹配的内容保存下来 | (\d{4})-(\d{2}) 捕获年月 |
| |
逻辑或,匹配左边或右边的模式 | cat|dog 匹配 "cat" 或 "dog" |
避坑指南:我曾经在解析日志时,用 .* 匹配了一大段内容,结果因为贪婪匹配导致程序跑飞了。记住:* 和 + 默认是贪婪的,会尽可能多地匹配。如果不想贪婪,在后面加个 ?,比如 .*?。
1.4 正则表达式在C语言中的基本使用流程
在 C 语言中使用正则表达式,主要依赖 POSIX 标准库中的 <regex.h>。基本流程分三步:编译正则、执行匹配、释放资源。
我画了一张流程图,帮你理清整体逻辑:
嗯,这里要注意:regcomp() 的第三个参数 flags 很关键。我常用的几个 flag:
REG_EXTENDED:使用扩展正则语法(支持+、?、|等)。我建议你始终加上这个 flag,因为基本正则语法太弱了。REG_ICASE:忽略大小写。匹配时不用区分大小写。REG_NOSUB:不报告匹配位置。如果你只想知道「有没有匹配上」,加上这个能提高性能。
我的习惯:每次调用 regcomp() 后,我都会检查返回值。如果返回非 0,说明正则表达式写错了。这时候可以用 regerror() 获取详细的错误信息。别问我为什么强调这个——我曾经花了一下午调试一个正则,结果发现是少写了一个反斜杠。
1.5 一个完整的例子
光说不练假把式。下面这个例子演示了如何用 C 语言的正则表达式提取字符串中的数字:
#include <stdio.h>
#include <regex.h>
int main() {
regex_t regex;
regmatch_t pmatch[10];
char *pattern = "[0-9]+";
char *text = "我的工号是10086,密码是123456";
// 编译正则
if (regcomp(®ex, pattern, REG_EXTENDED) != 0) {
printf("正则编译失败\n");
return 1;
}
// 执行匹配
char *p = text;
while (regexec(®ex, p, 10, pmatch, 0) == 0) {
int start = pmatch[0].rm_so;
int end = pmatch[0].rm_eo;
printf("找到数字: %.*s\n", end - start, p + start);
p += end; // 移动指针继续匹配
}
// 释放资源
regfree(®ex);
return 0;
}
运行结果:
找到数字: 10086
找到数字: 123456
这个例子虽然简单,但包含了正则使用的完整流程。你想想看,如果不用正则,用 strstr() 加循环判断,代码得多写多少行?
总结一下:正则表达式是 C 语言文本处理的利器。掌握它,你就能用更少的代码处理更复杂的文本匹配任务。后面几章,我会带你深入每个细节,包括分组捕获、性能优化、常见坑点等。