一、正则表达式基础

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>。基本流程分三步:编译正则、执行匹配、释放资源。

我画了一张流程图,帮你理清整体逻辑:

C语言正则表达式使用流程 1. 编译正则 regcomp(®ex, pattern, flags) 2. 执行匹配 regexec(®ex, string, nmatch, pmatch, flags) 3. 释放资源 regfree(®ex)

嗯,这里要注意: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 语言文本处理的利器。掌握它,你就能用更少的代码处理更复杂的文本匹配任务。后面几章,我会带你深入每个细节,包括分组捕获、性能优化、常见坑点等。


公众号:蓝海资料掘金营,微信deep3321