正则表达式

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;
}
相关推荐
xuanzdhc1 小时前
Linux 基础IO
linux·运维·服务器
愚润求学2 小时前
【Linux】网络基础
linux·运维·网络
bantinghy2 小时前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志3 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手3 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器
忘了ʷºᵇₐ9 天前
Linux系统能ping通ip但无法ping通域名的解决方法
linux·服务器·tcp/ip
浩浩测试一下9 天前
渗透测试指南(CS&&MSF):Windows 与 Linux 系统中的日志与文件痕迹清理
linux·运维·windows·安全·web安全·网络安全·系统安全