正则表达式

1. 正则表达式------简介

字符串在计算机中是最为广泛的处理对象(html网页,URL,IP,名称,密码,邮箱...),字符串的组合形式非常多样化。

如:

数字字符串: 由任意多个0~9组成的

email邮箱字符串

IP地址字符串

网址

...

这些字符串都是我们进行程序设计的时候需要处理的数据,现在就有一个问题:

计算机要处理这些字符串,首先要使用某一种方式(能够被全世界所有程序员认可的一种规则)去描述这些要处理的字符串

如:

如果要提取一段字符串中的IP地址,是不是就需要一个"规则"来描述IP地址

如果符合这个"规则"的就认为它是IP地址

============>

描述字符串规则的一种语法(如何使用程序语言去描述一个符合特定规则的字符串)

正则表达式 Regular Expression

2. 正则表达式是什么

正则表达式本身也是一个字符串,只不过是用来描述某种"规则字符串"的字符串

脱离了具体语言的一些限制,被大多数程序语言认可,并且现在大部分的程序设计语言都支持正则表达式

可以使用一些"规则"去描述一些特定的字符串

perl

python

java

php

c/c++

C#

...

每一种语言实现正则表达式的时候规则略有不同(支持程度不相同)

=======>正则表达式的流派
https://blog.csdn.net/zjc156m/article/details/50773149

基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx 简称 BREs) <----

扩展的正则表达式(Extended Regular Expression 又叫 Extended RegEx 简称 EREs)

Perl 的正则表达式(Perl Regular Expression 又叫 Perl RegEx 简称 PREs)

知道常用的正则表达式规则

并且知道一些支持正则表达式的文本/字符串处理工具(grep/find/sed/awk)

grep: 可以查找文件中/目录中符合规则的内容
find: 可以查找符合规则的文件名
sed: 可以以行为单位处理文本文件的内容
awk: 以列为单位处理文本文件的内容

3. 正则表达式中常见的规则

正则表达式是描述某一种"规则字符串"的字符串

如:

十进制的数字字符串 [0-9]+

0-9\]+ 是形容"十进制数字字符串"的规则 规则也叫做模式-------\>是形容"十进制数字字符串"的模式 正则表达式也叫做"匹配模式(pattern)",由一组特殊含义的字符串组成,通常用来匹配和替换文本 **正则表达式中的字符,分为两种:** * **普通字符** **只代表自己本身含义的字符(没有任何的特殊意义)** * **元字符** **由特殊含义的字符(不代表本身),如果要让他代表本身,需要加转义字符"\\"** 正则表达式中常见的元字符(需要记忆) **.****匹配任意的单个字符** **"m.n" 查找以m开头,中间有一个任意的字符,以n结尾的字符串** **"m..n" 查找以m开头,中间有两个任意的字符,以n结尾的字符串** **"m..n" 查找以m开头,中间有三个任意的字符,以n结尾的字符串** **......** **\[\] 字符组,虽然本身含有两个字符,但是仍然只匹配单个字符,而且能够匹配的字符都 都在\[\]中列举出来了** **\[\]仅仅匹配括号内列举出来的一个字符** **如: 表示一个能够组成16进制数字的字符 \[0123456789ABCDEFabcdef\] // 虽然有这么长,但是只表示一个字符** **grep -nar \[abc

查找a或者b或者c
grep -nar "[abc][012345]"
a0 a1 a2 a3 a4 a5
b0 b1 b2 b3 b4 b5
c0 c1 c2 c3 c4 c5**

[] 内部也有一个元字符 -

- 在中括号内部用于连接ASCII码连续的数字字符

**如: 表示一个能够组成16进制数字的字符

0123456789ABCDEFabcdef\] // 虽然有这么长,但是只表示一个字符 =======\> \[0-9A-Fa-f\]** **grep -nar "\[abc\]\[012345\]" ======\> grep -nar "\[a-c\]\[0-5\]"** **\[\^\] 排除字符组,匹配单个字符,匹配除了\[\]内部列举出来的所有单个字符** **如: 非十进制数字字符 \[\^0-9\]** **\\d** **digital 匹配单个的十进制数字字符 \\d -------\> \[0-9\]** **\\D****匹配单个的非十进制数字字符 \\D -------\> \[\^0-9\]** **\\w word 匹配单个字母和数字, _ \\w------\>\[a-zA-Z0-9_\]** **\\W** **匹配单个非字母和数字, _** **\\W------\>\[\^a-zA-Z0-9_\]** **\\s 匹配任意的空白符号(回车,换行,空格,tab) \\s -----\>\[\\f\\n\\t\\r\\b\\v\]** **\\S****匹配任意的非空白符** **\\S-----\>\[\^\\f\\n\\t\\r\\b\\v\]** **+** **匹配一个或者多个先前字符(模式)** **如: 09+ // +的前面是一个9 -----\>字符 09 099 0999 09999 ..... \[0-9\]+ // +的前面是一个模式 -----\>规则 匹配一个或者多个数字 \[0-9

0-9\]\[0-9

0-9\]\[0-9\]\[0-9

0-9\]\[0-9\]\[0-9\]\[0-9

.....**

* 匹配0个或者多个先前字符(模式)
如: 09* // *的前面是一个9 ----->字符
0
09
099
0999
09999
.....

**[0-9]* // *的前面是一个模式 ----->规则
空字符串

0-9

0-9\]\[0-9

0-9\]\[0-9\]\[0-9

0-9\]\[0-9\]\[0-9\]\[0-9

.....**

**? 匹配0个或者1个先前字符(模式)
如: 09? // ?的前面是一个9 ----->字符
0
09

0-9\]? // ?的前面是一个模式 -----\>规则 空白 0 1 2 3 ...** **9** **{数字} 匹配固定数量的先前模式 如: 9{3} 999** **\[0-9\]{3} 000 001 ... 999 {最小数量,最大数量} 匹配至少最小数量,至多最大数量的先前模式 如: 9{1,3} 9 99 999** **\[0-9\]{1,3} 0 1 2 3 4 5 .... 9 00 01 02 .... 99 000 001 002 003 .....999 {最小数量,} 匹配至少最小数量,至多无限制的先前模式** **如: abc{1,} abc abcc abccc abcccc .... () 把括号内的东西当成一个整体(子模式) 如: (abc){1,} abc abcabc abcabcabc ..... (\|) 括号内部的内容多选1 如: (abc\|123){2} abcabc 123123 abc123 123abc** **\\ 转义字符, \\+元字符,表示元字符本身 如: \\+ 表示查找+** **\\+{3}** 主要使用正则表达式的地方是shell脚本或者其他的一些字符串文本处理工具(grep,find,awk,sed) 标准的C语言对正则表达式有一定的支持

  1. 练习

(1)描述一个小于2^32的十六进制的数字字符串

0x0

0x1

....

0xffffffff

-------->

0[xX][0-9a-fA-F]{1,8}

(2)如何描述一个IP地址字符串呢? IP: 1~255.0~255.0~255.0~255

5. 标准的C库对正则表达式的支持

在C语言代码中也可以使用正则表达式描述或者匹配规则字符串

例子:

写一个代码,找出一个普通字符串中所有的IP地址

正则表达式描述IP地址:

(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])

一组函数 regcomp regexec regerror

cpp 复制代码
NAME
    regcomp, regexec, regerror, regfree - POSIX regex functions

SYNOPSIS
    #include <sys/types.h>
    #include <regex.h>
cpp 复制代码
regcomp: 是用来编译正则表达式字符串的

正则表达式本身也是一个字符串,需要把这个字符串转换成一个能够表示正则表达式规则的类型(regex_t)
regex_t类型的变量就可以表示一个编译好的正则表达式

原始的正则表达式------->regex_t类型的变量

int regcomp(regex_t *preg, const char *regex, int cflags);

    preg: 指针,指向一个可用的空间,用来保存编译好的正则表达式的
    regex: 指针,指向要编译的正则表达式原始字符串
    cflags: 编译标志,使用位域实现(整数中的某些位表示特定的功能)
                REG_EXTENDED:使用扩展的正则表达式语法编译这个规则 
                REG_ICASE:ignore case 忽略大小写 
                REG_NOSUB:不包含子模式
            如果在编译的时候,需要使用扩展的正则表达式语法并且需要忽略大小写 
                REG_EXTENDED | REG_ICASE

待查找字符串: "abc192.168.31.abcabc45.123.168.31abcabcxxxx192.168.31.123xxxx01.199.10.1"         

正则表达式规则:
    (1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])
                   
目标是IP地址,一般认为是总模式(最终的查找目的)   192.168.31.123
    [44,58)
                    
用小括号括起来的叫做子模式,上面的规则中有4个子模式
    192    第1个子模式
    168    第2个子模式  
    31     第3个子模式
    123    第4个子模式

返回值:
    编译成功返回0
    编译失败返回一个错误码,这个错误码表示一个失败原因,需要使用regerror这个函数去解析
cpp 复制代码
regerror是用来把regcomp/regexec执行返回的错误码,转换成对应的错误字符串的

size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);

    errcode: 你要转换的错误码,是regcomp/regexec失败时的返回值
    preg: 编译好的正则表达式地址
    errbuf: 指针,指向一段可用的空间,用来保存转换后的错误信息字符串的
    errbuf_size: errbuf指向的可用空间的大小,防止出现内存越界的错误

    返回值: 返回填充到errbuf这一段空间中的错误字符串的长度
cpp 复制代码
regexec是用来在string指向的字符串中,查找preg指向的正则表达式规则

匹配结果使用regmatch_t类型来描述,表示目标字符串(IP)在母串中的位置

typedef struct {
    regoff_t rm_so; // 匹配的起始偏移量
    regoff_t rm_eo; // 匹配的终止偏移量
} regmatch_t; // 用来说明匹配的目标在母串中的具体位置

int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], 
        int eflags);

    preg:需要匹配的正则表达式(经过regcomp编译过的)
    string:原始的等待匹配的母字符串,待查找字符串
    nmatch:正则表达式中有多少个匹配模式(总模式数量 + 子模式数量)
    pmatch:保存匹配到的结果的位置(数组)
    pmatch的大小至少为nmatch
    每一个模式都需要使用一个regmatch_t的数据类型去描述位置
    总共有多少个模式就有多少个regmatch_t
    eflags:匹配标志,是否匹配行首或者行尾(一般为0)

返回值:
    成功匹配到返回0
    如果失败返回REG_NOMATCH

如果一个待匹配的字符串中有多个结果------>循环多次匹配

cpp 复制代码
void regfree(regex_t *preg); // 用来释放preg这个数据中的空间

代码实现:

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>

// 待查找的目标字符串
char *str = "abc192.168.31.abcabc45.123.168.31abcabcxxxx192.168.31.123xxxx01.199.01.1";

// 使用正则表达式描述你需要查找的规则
// 在C语言的字符中,\本身就是一个转义字符  \.-------> \\.
char *reg = "(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])";

int main() {
    // 转换原始正则表达式--->regex_t
    regex_t reg_t;
    int res = regcomp(&reg_t, reg, REG_EXTENDED | REG_ICASE);
    if (res != 0) {
        printf("regcomp failed!\n");
        // 可以使用regerror解析失败的原因
        char buf[1024] = {0};
        regerror(res, &reg_t, buf, 1024);
        printf("buf:%s\n", buf);
        return -1;
    }
    printf("regcomp success!\n");
    // 执行转换之后的正则表达式规则(使用正则表达式规则去待查找字符串中做匹配)
    regmatch_t pmatch[5] = {0};
    int offset = 0;
    while (1) {
        res = regexec(&reg_t, str+offset, 5,pmatch,0);
        if (res == 0) { // 匹配到了字符串,打印结果 
            printf("找到了一个结果:\n");
            for(int i = 0; i < 5; i++) {
                printf("[%d,%d)\n", pmatch[i].rm_so + offset, pmatch[i].rm_eo + offset);
            }
            offset += pmatch[0].rm_eo;
        } else if (res == REG_NOMATCH) {
            printf("REG_NOMATCH\n");
            break;
        } else { 
            printf("regcomp failed!\n");
            // 可以使用regerror解析失败的原因
            char buf[1024] = {0};
            regerror(res, &reg_t, buf,1024);
            printf("buf:%s\n", buf);
            return -1;
        }
    }
    regfree(&reg_t);
    return 0;
}
相关推荐
小呆瓜历险记11 分钟前
ubuntu 22.04搭建SOC开发环境
linux·运维·ubuntu
码农101号12 分钟前
Linux中shell流程控制语句
linux·运维·服务器
ajassi200016 分钟前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
暗夜潜行38 分钟前
ubuntu + nginx 1.26 + php7.4 + mysql8.0 调优
linux·运维·ubuntu
IU宝42 分钟前
Linux下基础IO
linux·运维·服务器
鹅是开哥1 小时前
ZZU-ARM汇编语言实验2
linux·运维·服务器
Cyrus_柯1 小时前
网络编程(数据库:SQLite)
linux·c语言·数据库·sqlite
AlenTech1 小时前
Linux 系统可视化管理工具
linux·运维·服务器
头发够用的程序员1 小时前
小米玄戒O1架构深度解析(二):多核任务调度策略详解
android·linux·arm开发·智能手机·架构·手机
韭菜钟2 小时前
在Linux下使用vscode使用交叉编译工具链的gdb对core文件进行堆栈、变量查看
linux·运维·vscode