正则表达式(C、C++、Python、Shell)
撰写本文档的初衷本来是想介绍正则表达式怎么写,但是百度一搜,正则表达式的教程的质量已经相当高,我便不在班门弄斧了。正则表达式是一种方法,在不同的语言中,它的应用样式可能会不同,但是方法是一样的,只要了解好标准的正则表达式如何写,就能应付其他语言下的正则表达式样式。
那本文主要收集了常用编程语言下的应用样式 ,让大伙认识正则表达式在不同语言下的应用,至于基础,请大伙移步专业的教程。https://www.runoob.com/regexp/regexp-tutorial.html
一、正则表达式类型
正则表达式分为:①基础正则表达式 ②扩展正则表达式
基础正则表达式是最先提出和应用的,后来发现有些情况匹配能力有限,所以加入了扩展正则表达式;
比如说有这么一段字符串:“A=1,B=2,C=3,D=4“
我们计划把B的值提取出来,用基础正则匹配“B=”和","中间的,但是却提取出“2,C=3“
这时候需要拓展正则表达式中的贪婪模式(尽可能多)和非贪婪模式(尽可能少)来解决
加入扩展正则表达式,在基础正则表达式的匹配结果上进一步进行过滤。
在线验证正则表达式的工具:
https://tool.oschina.net/regex/
二、不同编程语言下的应用
1、C语言
在c语言中,用regcomp、regexec、regfree 和regerror处理正则表达式。处理正则表达式分三步:
①编译正则表达式,regcomp
②匹配正则表达式,regexec
③释放正则表达式,regfree
/*
函数说明:Regcomp将正则表达式字符串regex编译成regex_t的形式,后续regexec以此进行搜索。
参数说明:
Preg:一个regex_t结构体指针。
Regex:正则表达式字符串。
Cflags:是下边四个值或者是他们的或(|)运算。
REG_EXTENDED:使用POSIX扩展正则表达式语法解释的正则表达式。如果没有设置,基本POSIX正则表达式语法。
REG_ICASE:忽略字母的大小写。
REG_NOSUB:不存储匹配的结果。
REG_NEWLINE:对换行符进行“特殊照顾”,后边详细说明。
返回值:
0:表示成功编译;
非0:表示编译失败,用regerror查看失败信息
*/
int regcomp(regex_t *preg, const char *regex, int cflags);
/*
函数说明: Regexec用来匹配正则文本。
参数说明:
Preg:由regcomp编译好的regex_t结构体指针,
String:要进行正则匹配的字符串。
Nmatch:regmatch_t结构体数组的大小
Pmatch:regmatch_t结构体数组。用来保存匹配结果的子串位置。
regmatch_t结构体定义如下
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
rm_so,它的值如果不为-1,表示匹配的最大子串在字符串中的起始偏移量,rm_eo,表示匹配的最大字串在字符串的结束偏移量。
Eflags: REG_NOTBOL和REG_NOTEOL为两个值之一或二者的或(|)运算,稍后会介绍。
返回值:
0:表示成功编译;
非0:表示编译失败,用regerror查看失败信息
*/
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
/*
函数说明:用来释放regcomp编译好的内置变量。
参数说明:
Preg:由regcomp编译好的regex_t结构体指针。
*/
void regfree(regex_t *preg);
/*
函数说明:Regcomp,regexec出错时,会返回error code并且为非0,此时就可以用regerror得到错误信息。
参数说明:
Errcode:Regcomp,regexec出错时的返回值
Preg:经过Regcomp编译的regex_t结构体指针。
Errbuf:错误信息放置的位置。
errbuf_size:错误信息buff的大小。
*/
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
//比如字符串:<html>value</html>,想要匹配固定字符之间的内容:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
char ebuff[256]; //装载错误信息
int ret;
int cflags;
regex_t reg;
cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
char *test_str = "Hello World";
char *reg_str = "H.*";
ret = regcomp(®, reg_str, cflags); //编译
if (ret) { //编译失败
regerror(ret, ®, ebuff, 256);
fprintf(stderr, "%s\n", ebuff);
regfree(®);
return -1;
}
ret = regexec(®, test_str, 0, NULL, 0); //匹配
if (ret) { //匹配失败
regerror(ret, ®, ebuff, 256);
fprintf(stderr, "%s\n", ebuff);
regfree(®);
return -1;
}
regerror(ret, ®, ebuff, 256); //装载匹配成功结果
fprintf(stderr, "result is:\n%s\n", ebuff);
regfree(®); //释放
return 0;
}
2、C++
在C++中利用正则表达式需要regex库,在头文件<regex>中包含了多个我们使用正则表达式时需要用到的组件,大致有:
regex |
表示有一个正则表达式类,比如:regex pattern("(.{3})(.{2})_(\d{4})!") |
regex_match |
全文匹配,要求整个字符串符合正则表达式的匹配规则。用来判断一个字符串和一个正则表达式是否模式匹配,返回一个 bool 值,true 为匹配,false 为不匹配。匹配的含义是目标字符串必须完全和正则表达式相匹配,不能有多余的字符,如果需要部分匹配则应使用regex_search |
regex_search |
搜索匹配,根据正则表达式来搜索字符串中是否存在符合规则的子字符串。 能和正则表达式相匹配就返回true |
regex_replace |
替换匹配,即可以将符合匹配规则的子字符串替换为其他字符串。要求输入一个正则表达式,以及一个用于替换匹配子字符串的格式化字符串。这个格式化字符串可以通过转义序列引用匹配子字符串中的部分内容 |
sregex_iterator |
迭代器适配器,调用regex_search来遍历一个string中所有匹配的子串 |
smatch/match_results |
容器类,保存在string中搜索的结果。如果匹配成功,这些函数将成功匹配的相关信息保存在给定的smatch对象中 |
//比如字符串:Hello_2018
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main()
{
string str = "Hello_2018";
smatch result;
regex pattern("(.{5})_(\\d{4})"); //匹配5个任意单字符 + 下划线 + 4个数字
if (regex_match(str, result, pattern))
{
cout << result[0] << endl; //完整匹配结果,Hello_2018
cout << result[1] << endl; //第一组匹配(括号)的数据,Hello
cout << result[2] << endl; //第二组匹配(括号)的数据,2018
//result[]与result.str()这两种方式能够获得相同的值
cout<< result.str() << endl; //完整结果,Hello_2018
cout<< result.str(1) << endl; //第一组匹配(括号)的数据,Hello
cout << result.str(2) << endl; //第二组匹配(括号)的数据,2018
}
}
3、Python
实现网络爬虫必然离不开正则表达式,Python的正则表达式主要用re库实现。
移步专业教程:https://www.runoob.com/python/python-reg-expressions.html
//比如字符串:<html>value</html>,想要匹配固定字符之间的内容:
import re
str='<html>value</html>'
pattern=re.compile('<html>(.*)</html>') #匹配<html>与</html>之间的内容
result=pattern.findall(str)
print(result)
//贪婪和非贪婪匹配
str = "a123b456b"
print re.findall(r"a(.+?)b", str)
#输出['123']#?控制只匹配0或1个,所以只会输出和最近的b之间的匹配情况
print re.findall(r"a(.+)b", str)
#输出['123b456']
print re.findall(r"a(.*)b", str)
#输出['123b456']
//多行匹配
str = "a23b\na34b"
re.findall(r"a(\d+)b.+a(\d+)b", str)
#输出[]
#因为不能处理str中间有\n换行的情况
re.findall(r"a(\d+)b.+a(\d+)b", str, re.S)
#s输出[('23', '34')]
4、Shell
表达式在Linux编程中很常用,Shell脚本编程,文本处理的3把瑞士军刀sed,awk和grep都离不开正则表达式。Shell脚本中正则表达式的应用,可以参考我写的关于sed命令的文章。