注:以下内容均为作者个人见解,若有不正确的地方,还请读者斧正
同时如果对你帮助的话,还请点赞,收藏加关注,你们的支持是我最大的动力!
一、正则表达式
场景描述:比如你现在要写一个程序,其中有一个功能是匹配以一个日志文本中的IP地址。
IP地址:点分式的字符串
192.168.31.1
0.0.0.0
循环
然后一个字符一个字符的比对
ip_str[0] >= '0'&&ip_str[0] <='9'
这样写就会逻辑很复杂。
正则表达式就是用来描述某种规则字符的表达式,主要用于模糊搜索(查询)
要查找IP地址可以按照规则搜索
分析IP地址的规则:
- 一共分为四段
- 每一段至少一个数字至多三个数字
- 两端中间会有一个点
正则表达式里面字符串中字符分为两类:
- 普通字符:只代表自己
- 1 2 a b ...
-
元字符:不代表自己,有特殊含义(这个含义优先)
-
. * ? + \ ...
-
注意:元字符想要表示自己本身原来的意思的话,需要用(\)反斜杠进行转义
1.正则表达式中元字符
- . :表示匹配任意单个字符
- 示例:
表达式:[123456789] 表示在1~9的字符范围内去匹配一个
可以匹配到:1 2 3 4 5 6 7 8 9
表达式:[13579abcd] 表示在13579abcd的字符范围内去匹配一个
可以匹配到:1 3 5 7 9 a b c d
字符组可以用 - 来表示一个范围
[0123456789] 可以写成 [0-9]
[abcdefghij] 可以写成 [a-j]
其实就是按照ASCII码去列举范围
-
\w :表示匹配字母、数字以及( _ 下划线)
- 示例:
表达式:\w 表示在0~9、a-z、A-Z、_这些字符范围内去匹配一个
可以匹配到:0-9 a-z A-Z _
- + :表示匹配一个或多个先前字符或者是模式 (贪婪匹配)
- 示例:
表达式:\w+ 表示匹配一个或多个字母、数字、_
可以匹配到:1 11 1111 ....
:12 12222 12315646
....
表达式:1+ 表示匹配一个或多个1
可以匹配到:1 11 111 1111 ....
表达式:ab+ 表示先匹配a再匹配一个或多个b
可以匹配到:ab abb abbb ...
- * :表示匹配零个或多个先前字符或模式(惰性匹配)
- 示例:
表达式:ab* 表示先匹配a再匹配零个或多个b
可以匹配到:a ab abb abbb ...
- ? :表示匹配零个或一个先前字符或模式
- 示例:
表达式:ab?
可以匹配到:a ab
- {数字} :表示匹配固定数目的先前字符或模式
- 示例:
表达式:a{5} 表示完成匹配到5个a
可以匹配到:aaaaa
- {min,} :表示至少匹配 min 个先前字符或模式
- 示例:
表达式:a{5,} 表示至少匹配到5个a
可以匹配到:aaaaa aaaaaaaa aaaaaaaaa ....
- {min,max} :表示至少匹配 min 个至多匹配 max 个先前字符或模式
- 示例:
表达式:a{2,3} 表示至少匹配2个a,至多匹配3个a
可以匹配到:aa aaa
- () :表示子模式,其内部表达式看成一个整体
- 示例:
表达式:(12+)+ 表示至少一个或多个(12或12222... )
可以匹配到:12 122122 12221222....
分析IP地址的规则:
- 一共分为四段
- 每一段至少一个数字至多三个数字
- 两段中间会有一个点
匹配IP地址的正则表达式:
192.168.31.1
0.0.0.0
// 先匹配点分式
\.\.\. # 表示匹配三个点
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}
完全可以写成:([0-9]{1,3}\.){3}[0-9]{1,3}
优化匹配IP的正则表达式
ip最大:255.255.255.255 广播地址
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} 这个存在问题
分析:
[12]? 最高位要么是1要么是2要么有要么没有
[0-9]{1,2} 至少一个最多两个
[12]?[0-9]{1,2} 一段的
[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}
2.C/C++对于正则表达式的支持
C/C++标准库中,对于正则表达式有一定的支持
需求:
- 写一个C++程序,来获取一个字符串中的点分式字符串。
-
char *GetIPV4Address(const char *src_str) { } /* 比如: 输入:adsajdalkdj192.168.31.1asdjalkdj 输出:192.168.31.1 */
3.正则表达式的API接口
-
#include <regex.h> // 正则表达式的接口头文件 int regcomp(regex_t *preg, const char *regex, int cflags); int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags); size_t regerror(int errcode, const regex_t *preg, char *errbuf,size_t errbuf_size); void regfree(regex_t *preg);
4.编译正则表达式
int regcomp(regex_t *preg, const char *regex, int cflags);
功能:
用来编译 "正则表达式" 的。编译完成的政协表达式用regex_t 类型表示
"正则表达式"原始字符串(手写的正则表达式)编译完成之后 regex_t 类型
@preg:
regex_t *类型
指向空间,就是用来保存编译后的正则表达式的。
@regex:
const char *类型
指向空间是存储原始字符串(正则表达式)的。没有被编译过的正则表达式字符串
@cflags:
int 类型
标志位:
REG_EXTENDED 用来扩展正则表达式的语法
REG_ICASE ignore case 忽略大小写
REG_NOSUB 不包含子模式
以上标志位都是可以通过|运算符进行结合的
@return:
返回值为0表示无错误。
返回值不为0表示错误,该返回值为一个错误码,需要通过regerror去进行错误解析
5.进行正则表达式的匹配
int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t
pmatch[], int eflags);
功能:
用于匹配正则表达式所描述的模式
@preg:
正则表达式,匹配模式。(也就是编译好了正则表达式,一般由regcomp赋值)
@string:
const char *类型
原始字符串(母串)。需要进行匹配的原始字符串
@nmatch:
size_t 类型
提前告知有多少个模式,总模式=(1个)+子模式数量
@pmatch:
regmatch_t 类型数组
描述匹配结果,返回的是再母串中匹配到的下标范围,其实下标~结束下标
typedef struct
{
regoff_t rm_so; // rm_so : start offset 起始位置下标
regoff_t rm_eo; // rm_eo : end offset 终止下标
} regmatch_t;
模式匹配信息数组
数组大小为nmatch大小
一个模式就需要有一个regmatch_t
指定了多少模式就有多少个regematch_t
@eflags:
int 类型
表示十分匹配行首或行尾,一般为0
@return:
返回值为0表示匹配到了数据
返回值为REG_NOMATCH表示失败
6.错误解析
size_t regerror(int errcode, const regex_t *preg, char *errbuf,size_t
errbuf_size);
功能:
用来吧regcomp和regexec函数执行返回的错误进行解析的,转换为错误信息字符串的
@errcode:
int 类型
错误码。也就是需要进行解析的错误码
@preg:
const regex_t *类型
编译好的正则表达式
@errbuf:
char *类型
指向的空间用于保存解析错误信息之后,存储错误信息字符串的
@errbuf_size:
size_t 类型
表示errbuf大小
@return:
返回值为填充到errbuf中的错误提示信息的字符串长度
7.正则表达式清理
void regfree(regex_t *preg);
/*
功能:
是否preg指向的那个空间的
@preg:
就是编译后的正则表达式
*/
二、Makefile工程管理文件
Makefile 可以自动化编译工程文件
当我们执行 make 命令的时候,就会去找当前目录下对应的 Makefile 文件进行执行。
make :没有指定可执行文件名的时候,默认在当前路径下去找, Makefile 或是 makefile
make target :指定执行指令生成
1.Makefile文件的执行命令格式
- 目标文件:依赖文件列表
- 目标文件 :需要生成的文件(demo文件)(可执行文件/.o文件/库文件)
- 依赖文件列表 :生成目标文件(demo文件)需要哪些文件才能生成(一般是.cpp文件)
注意: Makefile 中可以自己定义变量的,但是变量是没有类型,一般作为字符串使用
2.Makefile的变量的定义
语法: 变量名 赋值符号 值
- 就地赋值:简单赋值
- 变量名:=值
- 示例: VALUE:="abcd"
- 递归赋值:依赖于后面的变量来赋值,展开赋值
- 变量名=值 #向后展开
- 示例:
-
A=$(B) B:="123" //--- A=$(B) ---> 需要执行到B:="123"
- 追加赋值:在原有值的基础上,追加值进去
- 变量名+=值1 值2 值3 值4 ...
- 示例: A+= "123" "456" "789"
- 条件赋值:当符合条件的时候才会进行赋值
- 变量名?=值 #当变量没有被定义或者没有被赋值的时候,才会进行复制
- 示例: A?="123"
3.变量的使用
语法: $(变量名)
示例:
- VALUE = 10 创建变量 VALUE
- $(VALUE) 引用变量 VALUE 的值
4.自动变量
- $* :不包含扩展名的目标文件名字
- $@ :目标文件的完整名称
- $+ :所有依赖文件名,以空格分开,可能会包含重复的依赖文件
- $^ :所有依赖文件名,以空格分开,不包含重复文件
- $< :第一个依赖文件名
- $? :所有比目标文件晚的依赖文件,以空格分开
5.Makefile的函数
- willdcard :文件名展开函数
- 只能带一种参数,就是提供文件名的通配方式
- 示例: SRC+=$(wildcard *.cpp) :表示把当前目录下的所有 .cpp 的文件全部获取到 SRC 这个变量里面
- 注意:目录是相对 Makefile 而言,而不是针对终端而言
- 而且不会包括子目录
- patsubst :字符串替换函数
- 带三个参数,分别为:
- 原有格式,替换格式,需要替换的字符串
- 示例: OBJ+=$(patsubst %.sh,%.cpp,$(SRC))
- 将 SRC 变量中的文件名由 .sh 结尾改为 .cpp 结尾
- 注意:并不会实际改变文件真正的名字,只是把 SRC 里面的值改变
- % :通配符,只匹配 Makefile 文件中的字符串
- 带三个参数,分别为:
注意:*是通配符任意字符。%通配符只能匹配Makefile文件中的任意字符。
注意:
- Makefile文件用tab缩减来描述包含关系。
- 指令要定格写
- 规范:变量名一般都会使用大写
- Makefile文件没有后缀(扩展名)
- 可以开头大写,也可以小写,但是执行make的时候优先选择大写的Makefile文件
- gcc在Makefile里面被定义成了cc变量
- 所有的Makefile文件需要放在你要编译的工程目录下才能起效果。