一文看懂正则表达式

Java 正则

JDK内置的正则表达式引擎:java.util.regex

用字符串来描述规则,用于匹配字符串的一种机制

最简单的代码示例

String input = "1926";
String regex = "19\\d{2}";
System.out.println(input.matches(regex)); //true

//注意String 的matches方法,参数中的reg字符串,必须和input完全匹配
//不能用matches()来判断输入字符串中,是否包含符合正则规则的子串
String input2 = "aa1926bb";
System.out.println(input2.matches(regex)); //false

//String里的matches()方法,实际也是利用了Pattern和Matcher来实现的
//如果要多次使用一个正则表达式来匹配字符串
//直接调用String里的matches()方法效率较低,因为每次都会生成一个Pattern和Matcher
//所以,此时可以先将正则字符串编译成Pattern,在用Matcher的matches方法去匹配

若需要在一个很长的字符串中寻找所有满足匹配规则的子串,代码实例如下

String regx = "[1][3,4,5,7,8]\d{9}";
Pattern p = Pattern.compile(regx);
String input = "alonglonglongstring13822221111###13512341234..32";
Matcher m = p.matcher(input);
while(m.find()){
    System.out.println(m.group()); 
    //group()默认调用group(0)
}

//利用Matcher的group(n)可以快速提取子串,分组匹配
String regzz = "([a-zA-Z]{3})(\\d{5})";
String inputzz = "Leo456789###Ann123456##1";
Pattern p2 = Pattern.compile(regzz);
Matcher m2 = p2.matcher(intputzz);
while(m2.find()){
    String whole = m2.group(0); //0表示匹配整个字符串     Leo456789
    String g1 = m2.group(1);    //1表示匹配的第1个子串    Leo
    String g2 = m2.group(2);    //2表示皮皮诶的第2个子串  456789
}

//利用正则表达式来分割字符串
String input = "hello,apple;banana	world yogurt";
String reg = "[\\s,;]+";
String[] strs = input.split(reg);
for(String item : strs){
    System.out.println(item);
    //hello
    //apple
    //banana
    //world
    //yogurt
}

//利用正则表达式来进行查找
String str = "The shark is one of the most dangerous animals in the world.";
Pattern pattern = Pattern.compile("the",Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
	String substr = str.substring(matcher.start(),matcher.end());
	System.out.println(substr);
    //The
    //the
    //the
}

//利用正则来做替换
//如,去除多余的空格
String str = "Hello world    this         is    yogurt.";
String simple = str.replaceAll("\\s+"," ");
System.out.println(simple);  //Hello world this is yogurt.

//正则后向引用
String str = "Hello world";
String res = str.replaceAll("(\\w+)","#$1#");  //$1 表示向前匹配第一个捕获组
System.out.println(res);  //#Hello# #world#

正则基础

元字符

  • \b :代表单词的开头或结尾,即单词的分界处,单词通常由空格标点符号换行符 来分隔,但\b不匹配这些分隔符,它只匹配一个位置

  • \B : 匹配非单词的边界位置

  • ^ :匹配字符串开始的位置

  • $ :匹配字符串结束的位置

  • \d :匹配一个数字,相当于[0-9]

  • \D :匹配一个非数字,相当于[^0-9]

  • \w :匹配字母、数字、下划线,相当于[A-Za-z0-9_]

  • \W :与\w相反,相当于[^A-Za-z0-9_]

  • \s :匹配任一空白符,如空格符,制表符,换行符,相当于[\f\n\r\t\v ] (注意 :末尾还有一个空格)

  • \S :匹配任一非空白符,相当于[^\f\n\r\t\v ]

  • \n :表示"新行"

  • \r :表示"回车"

  • . :匹配除换行符(\n\r)以外的任一单个字符

    举例:

    • [012]:匹配一个数字,数字可以是0,1,或2

    • [abdf] :匹配一个字母,字母可以是a,b,d,或f

    • [.?!*] :匹配 . ? ! * 四个字符中的任一一个标点(在[ ]内不需要转义)

限定符

  • * :匹配前面的子表达式0次或多次,相当于{0,}
  • + :匹配前面的子表达式1次货多次,相当于{1,}
  • ? :匹配前面的子表达式0次或1次,相当于{0,1}
  • {n} :n是一个非负整数,匹配前面的子表达式n次
  • {n,m} :m≥n≥0,最少匹配n次,最多匹配m次
  • {n,} :n≥0,最少匹配n次(注意 :没有{,m}的写法,请写成{0,m}

Examples:

如下的正则表达式:

\(?0\d{2}[)-]?\d{8}

分析:首先一个转义字符 \(表示匹配( ,后面?表示0次或1次,然后是2个数字,接着是[)-]? 表示匹配)- 字符 0次或1次,最后是8个数字

它能够匹配诸如 (010)88886666022-2233445501212345678等字符串,然而,它也能匹配到诸如 (0108888666 (010-8888666 010)8888666不符合预期规则的字符串,要解决此问题,需要用到分支条件

分支条件

指的是有几种规则,满足其中任意一种规则,都能完成匹配,使用|把不同的规则分开,例子如下:

例1

0\d{2}-\d{8}|0\d{3}-\d{7}

能匹配010-123456780376-1234567 这两种类型的电话号码

注意|表示完全分开两种规则,而不是就近分隔某一种,就近分隔需要用到`()

例2

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}

匹配3位区号,其中区号可以用小括号括起来,也可以不用,区号和本地号之间,可以用连字符或空格间隔,也可以没间隔

注意:分支条件会从左往右的测试每个条件,一旦满足某个条件,就不会继续管其他条件了(短路运算),如:

\d{5}-\d{4}|\d{5} 这个表达式用于匹配美国邮编,美国邮编要么是5位数字,要么是连字符间隔的9位数字,如果将正则写成如下

\d{5}|\d{5}-\d{4} 则只能匹配5位的邮编,或者9位邮编的前5位

所以要特别注意分支条件的放置顺序

分组

我们已经能实现重复单个字符的匹配,如\d{8}匹配8个数字,如果想要重复多个字符,可以使用分组,例子如下:

(\d{1,3}.){3}\d{1-3} 简单的IP地址匹配表达式,重复匹配子表达式 \d{1,3}. 3次,但是它也能匹配到诸如255.888.999.777这样的无效IP地址,正确的IP地址匹配表达式如下(不唯一):

^((25[0-5]|2[0-4]\d|[1]\d{2}|\d{1,2})\.){3}(25[0-5]|2[0-4]\d|[1]\d{2}|\d{1,2})$

注意分支条件的放置顺序

反义

查找除某个能简单定义的字符之外的字符

常用的反义字符(前面几个是大写字母):

\W : 匹配任意不是字母,数字,下划线的字符

\S :匹配任意不是空白符的字符

\D :匹配任意非数字的字符

\B : 匹配不是单词开头和结尾的位置

[^x] : 匹配除x以外的任意字符

[^aeiou] :匹配除a,e,i,o,u以外的任意字符

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的内容,此分组捕获到的内容,默认有一个组号(从左到右,捕获到的分组组号依次为1,2,3…)

后向引用可以重复搜索前面某个分组已捕获到的文本,如\1表示分组1捕获到的文本,如\b(\w+)\b\s+\1\b ,可以匹配到如hello hello

分组的名字也可以自己指定,使用(?<myWord>\w+)这样的形式来指定分组名称,其中的尖括号也可以用单引号来代替,如(?'myWord'\w+),在之后若要引用该分组,使用\k<myWord>,或\k'myWord'所以要捕获并使用这个分组,如下

\b(?<myWord>\w+)\b\s+\k<myWord>\b

注意:

JAVA中后向引用分为:

  • 在正则表达式中,使用\\1\\2来进行后向引用
  • 在正则表达式外,使用$1$2来进行后向引用
//代码示例
//正则表达式中使用后向引用
String regx1 = "([a-z]{5})[\\s\\w,]+\\1";
String input1 = "hello this is yogurt ,hello";
boolean f = input1.matches(regx1); //true

//正则表达式外使用后向引用
String regx2 = "([a-zA-Z]+)";
String input2 = "Wireshark Tomcat Zookeeper are funny names.";
String after = input2.replaceAll(regx2,"#$1#");
System.out.println(after);
//#Wireshark# #Tomcat# #Zookeeper# #are# #funny# #names#.

分组语法

代码描述
(exp)匹配exp,并捕获文本到自动命名的组里
(?<name>exp)匹配exp,并捕获文本到名称为name的组里
(?:exp)匹配exp,但不捕获匹配到的文本

零宽断言

用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说像\b^$ 那样用于指定一个位置,这个位置应该满足一定的条件。

  • 零宽正向断言(肯定断言)

    • 零宽正向先行断言 (?=exp)

      断言此位置的后面能匹配表达式exp,比如\b\w+(?=ing\b)能匹配以ing结尾的单词前面的部分

    • 零宽正向回顾断言 (?<=exp)

      断言此位置的前面能匹配表达式exp,比如(?<=\bpre)\w+\b能匹配以pre开头的单词后面的部分

  • 零宽负向断言(否定断言)

    • 零宽负向先行断言 (?!exp)

      断言此位置的后面不能匹配表达式exp,比如\d{3}(?!\d)匹配3个数字,并且这3个数字后面不能是数字

    • 零宽负向回顾断言 (?<!exp)

      断言此位置的前面不能匹配表达式exp,比如(?<![a-z])\d匹配1个数字,该数字前不能是小写字母

综合例子:

(?<=<(\w{3})>).*(?=<\/\1>) 能够匹配字母数为3的html标签,如<div></div>,<img></img>

注意 :零宽断言不允许含有不定长的表达式,如(?<=<(\w+)>).*(?=<\/\1>)

贪婪与懒惰

当正则表达式包含能接受重复的限定符(如*+{n,} ),通常的行为时(在使整个表达式得到匹配的前提下)匹配尽可能多的字符,例如若用表达式a.*b来匹配aababb,它将匹配整个字符串aababb,这被称为贪婪匹配。

然而有时我们需要匹配尽可能少的字符串,即需要懒惰匹配。此时在限定符后面加上一个问号? 即可转换为懒惰匹配。如用a.*?b去匹配aababb,则会匹配aab(1-3位的字符)和ab(4-5位)

为什么第一个匹配到的是aab(1-3位),而不是ab(2-3位)呢?因为正则表达式有另一条规则,最先开始的匹配拥有最高的优先权

代码说明
*?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n此以上,但尽可能少重复
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QRegExp是Qt框架中的一个类,用于进行正则表达式的匹配和处理。它提供了多种模式来匹配不同的字符串。其中,QRegExp::RegExp模式提供了一个简单的模式匹配语法,类似于shell的通配符,可以使用字符“\”进行转义。QRegExp::Wildcard模式类似于通配符,但具有Unix shell的行为。QRegExp::FixedString模式是一个固定字符串,相当于在字符串上使用RegExp模式,其中所有元字符都使用escape()进行转义。QRegExp::W3CXmlSchema11模式是由W3C XML架构1.1规范定义的正则表达式。\[1\] 在使用编辑框输入内容时,可以使用QRegExp类的正则表达式来限制用户输入的内容,以避免输入无关信息和防止SQL注入。QRegExp类的正则表达式可以对编辑框的输入内容进行限制和匹配。\[2\] 如果想要匹配特定的单词,可以使用括号将单词组合在一起,例如(mail|letter)。为了强制匹配的开始和结束都在单词的边界上,可以将正则表达式包含在\b单词边界断言中,即\b(mail|letter)\b。这个\b断言在正则表达式中匹配一个位置,而不是一个字符,一个单词的边界是任何的非单词字符,如一个空格、新行,或者一个字符串的开始或结束。\[3\] #### 引用[.reference_title] - *1* [QRegExp(正则表达式)](https://blog.csdn.net/qq_45303986/article/details/127871375)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【QT学习】QRegExp类正则表达式一文)](https://blog.csdn.net/qq_59134387/article/details/127182436)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Qt QRegExp 正则表达式](https://blog.csdn.net/m0_73443478/article/details/128483562)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值