正规式的概念及日常使用

基本概念

正规式:也叫正则表达式,是表示正规集的数学工具,是说明单词的模式的一种表示法,常用于在一个文件或字符里进行格式匹配。正则工作时以行为单位,一次处理一行。
元字符:正则表达式由普通字符和元字符组成。其中元字符不代表他们本身的字面意思,而是用于表达控制或通配符的功能,如下表所示:

元字符描述
^从开始行开始匹配.
$从末端开始匹配.
.句号匹配除了换行符以外的任意单个字符。
[ ]字符集。匹配方括号内的任意字符。
*匹配>=0个重复的在*号之前的字符。
\转义字符,用于匹配一些保留的字符 ,如`[ ] ( ) { } . * + ? ^ $ \
[^ ]否定字符集。匹配除了方括号里的任意字符
+匹配>=1 个重复的 + 号前的字符。
?标记?之前的字符为可选.
{n,m}匹配 num 个大括号之前的字符或字符集 (n <= num <= m).
(xyz)特征标群,匹配与 xyz 完全相等的字符串.
|或运算符,匹配符号前或后的字符.

Linux 三剑客

Linux 下普通命令无法使用正则表达式,仅三剑客(grep、sed、awk)支持。
通配符是大部分普通命令都支持的,用于查找文件或目录,而正则表达式用于通过文本处理工具三剑客命令在文件中过滤内容。
在 Linux 中,正则表达式分为两种:
①.基本正则表达式:BRE,对应的元字符有 ^$.[]*
②.扩展的正则表达式:ERE,即是在 BRE 基础上,增加上 (){}?+| 等字符

(1) grep

grep 是一种文本过滤工具,用于根据用户指定的 " 模式 (过滤条件)" 对目标文本逐行进行匹配检查,打印匹配到的行
语法:grep [options] [pattern] file
扩展正则必须使用 grep -E 才能生效,如若不加,则需要在特殊字符前加反斜杠 “”,标识为正则

参数选项含义
-v排除匹配结果
-n显示匹配行与行号
-i不区分大小写
-c只统计匹配的行数
-E使用扩展的正则表达式
–color=auto为 grep 的结果添加颜色
-w只匹配过滤的单词
-o只输出匹配的内容

(2) sed

sed 是一种字符流编辑工具,适合编辑匹配到的文本,默认不会直接修改源文件数据,而是会将数据复制到缓冲区中,修改也仅限于缓冲区中的数据
语法:sed [选项] [sed 内置命令字符] [输入文件]
如果需要使用扩展正则需要加上选项 -E

参数选项解释
-n取消默认 sed 的输出,常与 sed 内置命令 p 一起使用从而只输出匹配行
-i直接将修改结果写入文件,若不加 -i,sed 修改的是内存缓冲区数据
-e多次编辑,不需要管道符了
-E支持正则扩展
sed 的内置命令字符解释
aappend,对文本追加,在指定行后面添加一行/多行文本
dDelete,删除匹配行
iinsert,表示插入文本,在指定行前添加一行/多行文本
pPrint,打印匹配行的内容,通常 p 与 -n 一起用
s/正则/替代内容/g匹配正则内容,然后替换内容 (支持正则),结尾 g 代表全局匹配


(3) awk

awk 是格式化文本的利器,用于对文本进行较复杂的格式处理,默认以空格为分隔符,支持条件判断、数组、循环等功能,其默认使用 ERE
语法:awk [option] 'pattern [{action}]' file

内置变量解释
$n指定分隔符后,当前记录的第n个字段
$0完整的输入记录
FS字段分隔符,默认就是空格
NF分割后,当前行一共有多少个字段(列数)
NR当前记录数(行数)
参数解释
-F指定分隔字段符
-v定义或修改一个 awk 内部的变量
-f从脚本文件中读取 awk 命令

(4) 命令行通配符

命令行通配符与正规式中存在些许差异:

元符号作为通配符作为正规式
*匹配任意长度字符匹配>=0 个重复的在 * 号之前的字符
匹配任意单个字符标记?之前的字符为可选

也就是说,作为 Linux 通配符来讲,* 等价于正规式中的 .*? 等价于正规式中的 .

不同语言对 RE 的支持

C/C++

标准的 C 和 C++ 都不支持正则表达式,但有正则表达式的函数库提供这功能
C 语言中使用正则表达式一般分为三步:编译、匹配、释放

// 正则表达式头文件
#include<regex.h>
// 其中包含结构体
typedef struct {
    regoff_t rm_so;		// 存放匹配文本串在目标串中的开始位置
    regoff_t rm_eo;		// 存放匹配文本串在目标串中的结束位置
} regmatch_t;


// 编译正则表达式:把指定的正则表达式pattern编译成一种特定的数据格式(regex_t *compiled)
int regcomp (regex_t *compiled, const char *pattern, int cflags);
// 匹配正则表达式
int regexec (regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr[], int eflags);
// 释放正则表达式 
void regfree (regex_t *compiled);
// 当执行regcomp 或者regexec 产生错误时,可以调用以下函数返回一个包含错误信息的字符串。
size_t regerror (int errcode, regex_t *compiled, char *buffer, size_t length);

例如,匹配一个 Email 邮箱:

#include <stdio.h>
#include <sys/types.h>
#include <regex.h>

int main(int argc,char** argv)
{
    if (argc != 2) {
        printf("Usage: %s Text\n", argv[0]);
        return 1;
    }
    char * buf = argv[1];
   	const char * pattern = "^\\w+@(\\w+\\.)+[a-zA-Z]{2,}$";
	regmatch_t pmatch[1];
	regex_t reg;
	regcomp(&reg,pattern,REG_EXTENDED);
	if(regexec(&reg,buf,1,pmatch,0) == REG_NOMATCH)
		printf("No match\n");
	else{
		printf("匹配成功:");
		for(int i = pmatch[0].rm_so;i<pmatch[0].rm_eo;++i)putchar(buf[i]);
		printf("是一个合法的邮箱地址\n");
	}
	regfree(&reg);
	return 0;
}


C++ 中也有类似的函数,不过更为简洁

#include <iostream>
#include <string>
#include <regex>

using namespace std;

int main(int argc,char** argv)
{
    if (argc != 2) {
        printf("Usage: %s Text\n", argv[0]);
        return 1;
    }
    string line = argv[1];
    regex pattern(R"(^\w+@(\w+\.)+[a-zA-Z]{2,}$)");
    smatch result;
    if(regex_match(line,result,pattern)){
        cout<<" 匹配成功:"<<result[0]<<" 是一个合法的邮箱地址\n";
    }else{
        cout<<"No match\n";
    }
    return 0;
}

Java

Java 的正则表达式是由 java.util.regex 的 Pattern 和 Matcher 类实现的。Pattern 对象表示经编译的正则表达式。静态的 compile( )方法负责将表示正则表达式的字符串编译成 Pattern 对象。常用方法如下表所示

方法名描述
boolean matches(String regex)当前字符串是否匹配给定的正则表达式
String replaceAll(String regex,String replacement)使用 replacement 替换当前字符串中所有符号正则表 达式的内容
String[] split(String regex)根据给定的正则表达式拆分当前字符串
// 判断输入的手机号是否为13或者18开头
import java.util.Scanner;
public class EXjava {
 
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.print("请输入手机号:");
		String str = input.nextLine();
		String regex = "1[38]\\d{9}";//用正则表达式定义手机号规则
		boolean flag = str.matches(regex);
		System.out.println("手机号是:"+flag);
		input.close();
	}
}

// 分割年龄段
import java.util.Scanner;
public class EXjava {

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
 
        String age = "18-30";//定义年龄范围
		String regex = "-";
		String[] strArr = age.split(regex);//分割成字符串数组
		
		int startage = Integer.parseInt(strArr[0]);
		int endage = Integer.parseInt(strArr[1]);
		
		System.out.print("请输入年龄:");
		int a = input.nextInt();
		if(a >= startage && a <= endage) {
			System.out.println("Yes");
		}
		else {
			System.out.println("No");
		}
		input.close();
	}
}

Python

在使用正则表达式之前,需要导入 re 模块。

方法名描述
findall()找到re匹配的所有字符串,返回一个列表
search()扫描字符串,找到这个re匹配的位置(仅仅是第一个查到的)
match()判断re是否在字符串刚开始的位置(匹配行首)
sub()通过正则定位,替换字符串
#从初始位置开始匹配
import re

line = 'i can speak good english'
matchObj = re.match(r'(i)\s(\w*)\s(\w*).*',line)
if matchObj:
    print('matchObj.group() :',matchObj.group())
    print('matchObj.group() :',matchObj.group(1))
    print('matchObj.group() :',matchObj.group(2))
    print('matchObj.group() :',matchObj.group(3))
else:
    print('no match!')

import re

line = 'i can speak good english'
matchObj = re.search('(.*) (.*?) (.*)',line)
if matchObj:
    print('matchObj.group() :',matchObj.group())
    print('matchObj.group() :',matchObj.group(1))
    print('matchObj.group() :',matchObj.group(2))
    print('matchObj.group() :',matchObj.group(3))
else:
    print('no match!')

import re

line = 'i can speak good english'
speak = re.sub(r'can','not',line)
print(speak)
speak1 = re.sub(r'\s','',line) #替换所有空格
print(speak1)


提取图片地址

import re
s = """<img data-original="https://img02.sogoucdn.com/app/a/100520024/36189693dc8db6bd7c0be389f8aaddbd.jpg" src="https://img02.sogoucdn.com/app/a/100520024/36189693dc8db6bd7c0be389f8aaddbd.jpg" width="250" height="375" .jpg>"""
result1 = re.search(r"src=\"https.*.jpg\"",s)
print(result1.group())

result2 = re.search(r"src=\"(https.*.jpg)\"",s)
print(result2.group(1))

各种IDE对正规式的支持

vscode

捕获组合和替换模式
要创建带编号的捕获组,可以在正则表达式模式中用圆括号将子表达式括起来。 捕获按正则表达式中左括号的位置从左到右自动编号。 若要访问捕获的组,请考虑以下示例:

  • 在正则表达式中:请使用 \number。 例如,正则表达式 (\w+)\s\1 中的 \1 引用第一个捕获组 (\w+)
  • 在替换模式中:请使用 $number。 例如,已分组的正则表达式 (\d)([a-z]) 定义了两个组:第一个组包含一个十进制数字,第二个组包含一个 a 到 z 之间的字符。 该表达式在以下字符串中查找四个匹配项:1a 2b 3c 4d。 替换字符串 z$1 仅引用第一个组($1),并将该字符串转换为 z1 z2 z3 z4。



也可以为捕获组命名,而不依赖于捕获组的自动编号。 命名的捕获组的语法为 (?<name>subexpression)
若要访问命名的捕获组,请考虑以下示例:

  • 在正则表达式中:请使用 \k<name>。 例如,正则表达式 (?<repeated>\w+)\s\k<repeated> 中的 \k<repeated> 引用名为 repeated 且其子表达式为 \w+ 的捕获组。
  • 在替换模式中:请使用 ${name}。 例如,${repeated}

word

word 中并不使用正则表达式,而是使用通配符来查找和替换文字,具体如下所示:

描述类型示例
任一字符?s?t 可找到 “sat” 和 “set”
任何字符串*s*d 可找到 “sad” 和 “started”
单词开头<<(inter) 可找到 “interesting” 和 “intercept”,但找不到 “splintered”
单词结尾>(in)> 可找到 “in” 和 “within”,但找不到 “interesting”
指定字符之一[ ]w[io]n 可找到 “win” 和 “won”
此范围内的任一字符[-][r-t]ight 可找到 “right” 和 “sight”,范围必须是升序
除了括号内范围中的字符之外的任一字符[!x-z]t[!a-m]ck 可找到 “tock” 和 “tuck”,但找不到 “tack” 或 “tick”
前一个字符或表达式的 n 个匹配项{n}fe{2}d 可找到 “feed”,但找不到 “fed”
前一个字符或表达式的至少 n 个匹配项{n,}fe{1,}d 可找到 “fed” 和 “feed”
前一个字符或表达式的 n 到 m 个匹配项{n,m}10{1,3} 可找到 “10”、“100” 和 “1000”
前一个字符或表达式的一个或多个匹配项@lo@t 可找到 “lot” 和 “loot”

可以看到,其中? * < > ! @ 均与正则表达式有较大的不同,他们之间的关系如下:
* 等价于正规式中的 .*? 等价于正规式中的 .
< 等价于正规式中的 ^> 等价于正规式中的 $
[!] 等价于正规式中的 [^]@ 等价于正规式中的 +



Nodepad++

nodepad++同样支持正规式查找与替换,大部分规则与前面相同


但notepad++的换行符不太一样,是\r\n,值得注意。


同样,nodepad++支持捕获组合和替换模式

flex工具对正规式的支持

大多数 flex 程序都具有二义性,相同的输入可能被多种不同的模式匹配。 flex 通过两个简单的规则来解决它:

  • 词法分析器匹配输入时匹配尽可能多的字符串。
  • 如果两个模式都可以匹配的话,匹配在程序中更早出现的模式。
    以下是一个字符统计程序:
%{
    int chars = 0;
    int words = 0;
    int lines = 0;
%}
%%
[a-zA-Z]+       {words++;chars += strlen(yytext);}
\n                     {chars ++;lines++;}
.                        {chars++;}
%%

int main(int argc,char** argv){
    yylex();
    printf("lines:%8d,  words:%8d,  chars:%8d\n",lines,words,chars);
    return 0;
}

Everything 工具对正规式的支持

Everything 是速度最快的文件名搜索软件,其支持通配符和正则表达式搜索
首先需要开启 everything 工具在(字符串)查找时,对正则表达式功能的支持:
【菜单栏】⇒ 【Search】⇒ 勾选【Enable Regex】

  • ctrl + i:字符大小写敏感/不敏感
    通配符搜索:

    正则式搜索:
  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值