正则表达式入门
随着爬虫日益普及,很多人开始捡起了正则,做一些简单的信息提取处理,越来越多的个性化正则表达式的需求,可还是有很多人不知道怎么下手,无法编写出一个强壮的正则,毕竟看起来和乱码差不多。
老顾这里用几个问答小伙伴的例子,来简单说一下,正则的写法。
不过,本文不讲述正则的基本支持,有需要补课的小伙伴,可以看老顾以前的文章:
《文盲的正则表达式入门》
《揭开正则表达式的神秘面纱》
《python 正则使用详解》
《文盲的正则表达式入门,实战篇》
老顾之前发了个实战篇,本意是有小伙伴可以提问,结果老顾高估了自己的人气,所以一直也没有小伙伴提问,所以老顾干脆,从新从另外一个角度,来讲述一下怎么写正则表达式好了。希望大家看完本文,能够自己写出强壮的正则来。
示例讲解
1、java里正则表达式replaceAll
问答地址:https://ask.csdn.net/questions/7919433?spm=1005.2025.3001.5141
预期结果是abcd,为什么运行结果是ad啊
String s = "abbbbccccdddddddddd";
String s1 = s.replaceAll("((.)\\2)+", "$2");
问题描述:有字符串“abbbbccccdddddddddd”,期望所有连续重复的字符只保留一个
一个简单的问题,我们只需要把问题描述改成正则描述即可
连续的字符
判断连续的字符,需要用到分组,即将任意字符作为一个分组,然后后边跟随该分组引用
(.)(?:\1)*
这个正则片段中,第一个括号表示分组,第二个括号,老顾加上了?: 修饰,表示不参与分组,\1 表示引用第一个分组,然后对引用分组的结果加上一个长度修饰,老顾用的是*,表示0到多个,然后,因为默认长度模式是贪婪模式,所以,这个正则就算完成了,然后,在替换部分也加上引用就可以了。
正则测试
python 测试
import re
print(re.sub(r'(.)(?:\1)*','\\1','abbbbccccdddddddddd'))
abcd
print(re.sub(r'(.)(?:\1)*','\\1','aabaacadaadde'))
abacadade
javascript 测试
'abbbbccccdddddddddd'.replace(/(.)(?:\1)*/gi,'$1')
'abcd'
'aabaacadaadde'.replace(/(.)(?:\1)*/gi,'$1')
'abacadade'
题主问题讲解
题主在问答中,给出了自己的正则
((.)\2)+
这里需要注意模式问题哦,这个正则解读如下
字符串数据:abbbbccccdddddddddd
正则表达式:((.)\2)+
首先是 (.) 表示任意字符,并放入分组
然后是(.)\2 ,因为外层还有一个分组,所以内层分组的分组序号是2,这里表示引用前边的这个(.) 的匹配结果,也就是连续字符,比如 bb,比如 cc
再然后是对连续字符的分组 ((.)\2)+ 并有长度修饰,注意,长度修饰是贪婪模式哦,所以这个正则匹配到的内容是
bbbbccccdddddddddd,所以在使用替换的时候,这么一大串,会当做一个匹配来处理
最后替换引用的 $2,也就是第二个分组内容,是在上述匹配时,最后一个符合的内容
bb bb cc cc dd dd dd dd dd,那么最后一个符合分组的匹配是 dd,所以第二个引用的结果就是字母 d
所以破案了,题主的结果为什么是 ad
2、开发者遇到金额的校验
问答地址:https://ask.csdn.net/questions/7916098?spm=1005.2025.3001.5141
一个金额的正则校验,整数部分最多16位,以千分位展示,小数点后保留两位小数且第三位不可输入
看题主的意思,应该是一个数据验证,校验输入的数据是否符合金额的千分格式
正则描述
这个正则稍微复杂了一点,他的需求可以拆分为几个小的描述
1、整数部分最多16位
2、整数部分以千分位展示
3、小数点后保留两位小数
那么,逻辑就出来了,小数点后保留两位是必须得,整数部分长度是最大16位,然后每三位加一个逗号的样子,也就是21位
所以第一个片段,我们先验证字符串长度
^(?=[^\.]{1,21}(?=\.))
这个片段是什么意思呢?
^ 表示从字符串开头开始匹配
(?=) 表示右断言,或者说预搜索,表示从当前位置,后边的内容应该是这个样子的
[^\.]{1,21} 表示除了小数点之外,其他任意字符,长度在1到21之间
(?=\.) 后边又跟了一个右断言,表示上述长度的字符串后,必须跟一个小数点的样子
那么小数点之前的长度验证完成了,然后我们需要验证是否是千分位格式,这个时候就不用断言了
\d{1,3}(,\d{3})*\.\d{2}$
这个片段的意思就是
\d{1,3} 必须是1到3个数字开头
(,\d{3})* 后边跟上多个以逗号开头的三位数
\. 然后是必须有的小数点
\d{2} 然后是一个必须有的小数点后两位数
$ 最后是字符串结束
那么完整的正则就是
^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$
正则测试
python 测试
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','100'))
None
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','100.00'))
<re.Match object; span=(0, 6), match='100.00'>
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','100.000'))
None
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','1000.00'))
None
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','1,000.00'))
<re.Match object; span=(0, 8), match='1,000.00'>
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','10,000,000,000,000,000.00'))
None
print(re.search(r'^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$','1,000,000,000,000,000.00'))
<re.Match object; span=(0, 24), match='1,000,000,000,000,000.00'>
javascript 测试
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('100')
false
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('100.00')
true
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('100.000')
false
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('1000.000')
false
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('1,000.00')
true
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('1,000,000,000,000,000.00')
true
/^(?=[^\.]{1,21}(?=\.))\d{1,3}(,\d{3})*\.\d{2}$/gi.test('10,000,000,000,000,000.00')
false
3、java正则表达式匹配字符串
问答地址:https://ask.csdn.net/questions/7916062?spm=1005.2025.3001.5141
#java正则 #正则表达式分割#java断言
请教,想要根据 ( ) 外的 | 分割字符串,正则表达是该如何匹配呢?
例如:
%这个一个测试2|4\)11|(0jh|96)78|8\)k|字符串% 分割后应为 如果是 \( \) 则不不认为是括号
%这个一个测试2
4\)11
(0jh|96)78
8\)k
字符串%
正则描述
额,这个更加复杂了一点,比千分位验证的难不少。咱们先捋捋需求啊
1、对竖线前后内容进行分割
2、如果竖线在括号内则不进行分割
3、如果括号是被转义的,则不认为是括号
其实,这个需求读下来,还有一个隐藏的条件,就是如果括号不成对的时候,是很难处理的,好在题主没有这个说明,咱就当括号是严格匹配的。
这次,就不能一口气实现了,咱们分阶段实现好了
\|
print('\n'.join(re.split(r'\|','%这个一个测试2|4\)11|(0jh|96)78|8\)k|字符串% ')))
%这个一个测试2
4\)11
(0jh
96)78
8\)k
字符串%
先按照最基本的需求,以竖线分割字符串
然后,我们将竖线限定为括号外的竖线,括号内的忽略
\|(?=[^\)]*(?:$|\|))
print('\n'.join(re.split(r'\|(?=[^\)]*(?:$|\|))','%这个一个测试2|4\)11|(0jh|96)78|8\)k|字符串% ')))
%这个一个测试2|4\)11
(0jh|96)78|8\)k
字符串%
我们追加了一个右断言
(?=[^\)]*(?:$|\())
用来限定,竖线后边没有右括号,直到碰到另一个竖线或者字符串结束
[^\)]* 非右括号内容
(?:$|\|) 竖线或者字符串结束
然后,我们需要对转义的括号进行一下处理
\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))
print('\n'.join(re.split(r'\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))','%这个一个测试2|4\)11|(0jh|96)78|8\)k|字符串% ')))
%这个一个测试2
4\)11
(0jh|96)78
8\)k
字符串%
这次,我们是对原来的 [^\)] 部分进行了一些调整,指定 \\\( 和 \\\) 可以被匹配
这个时候,这个正则就符合题主的要求了
正则测试
python 测试
print(re.split(r'\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))','%这个一个测试2|4\)11|(0jh|96)78|8\)k|字符串% '))
['%这个一个测试2', '4\\)11', '(0jh|96)78', '8\\)k', '字符串% ']
print(re.split(r'\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))','用|分割所有内容|(括号里的|被忽视)|如果括号\\(转义|会忽略转义\\)的括号'))
['用', '分割所有内容', '(括号里的|被忽视)', '如果括号\\(转义', '会忽略转义\\)的括号']
javascript 测试
'%这个一个测试2|4\\)11|(0jh|96)78|8\\)k|字符串% '.split(/\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))/gi)
(5) ['%这个一个测试2', '4\)11', '(0jh|96)78', '8\)k', '字符串% ']
'用|分割所有内容|(括号里的|被忽视)|如果括号\\(转义|会忽略转义\\)的括号'.split(/\|(?=(?:[^\)]|\\\(|\\\))*(?:$|\|))/gi)
(5) ['用', '分割所有内容', '(括号里的|被忽视)', '如果括号\(转义', '会忽略转义\)的括号']
4、关于#正则表达式#的问题,如何解决?
问答地址:https://ask.csdn.net/questions/7907410/54127882?spm=1001.2014.3001.5501
正则表达式
匹配汉字中ABB类型的词组
import re
text = input()
words = re.findall(r'((.)(.)\3)',text)
print(words)
这个正则表达式能匹配ABB但是也能匹配三个相同的汉字,有没有办法让第一个汉字与第二个汉字不同
正则描述
哦吼,和第一个示例差不多的内容,不过要求前边还有一个不同的字符
来描述一下需求,很简单的
需要abb形式的内容,且a与b不得相同
那么我们已经学会分组,也学会断言了,实现起来还是很简单的
(.)(?!\1)(.)\2
多简单的实现,分组1后边不能跟和分组1相同的内容,分组2后边跟分组2相同的内容,这不就是 abb 形式了么
正则测试
python 测试
re.findall(r'((.)(?!\2)(.)\3)','add and all food zzz 宋甜甜,范若若,戚戚惨惨切切')
Out[24]:
[('add', 'a', 'd'),
('all', 'a', 'l'),
('foo', 'f', 'o'),
(' zz', ' ', 'z'),
('宋甜甜', '宋', '甜'),
('范若若', '范', '若'),
(',戚戚', ',', '戚'),
('惨切切', '惨', '切')]
javascript 测试
'add and all food zzz 宋甜甜,范若若,戚戚惨惨切切'.match(/((.)(?!\2)(.)\3)/gi)
(8) ['add', 'all', 'foo', ' zz', '宋甜甜', '范若若', ',戚戚', '惨切切']
大体实现了题主的需求了,一些 空格标点之类的也参与了进来,无伤大雅,自己把点修改成指定的字符集即可
进阶实现
在这个示例里,老顾自己写了个字符串,戚戚惨惨切切,那么,如果要匹配 abb 形式,其实应该是有两个结果:戚惨惨,惨切切。
而正常的正则在匹配时,每个字符只能参与一次匹配,如何才能让字符参与多次匹配呢?
其实应该有小伙伴反应过来了,使用右断言啊!右断言又叫预搜索可不是白叫的哦。
那么,最后的正则也就可以想到了,用预搜索来实现 abb 组合的检索
re.findall(r'(?=((.)(?!\2)(.)\3))','add and all food zzz 宋甜甜,范若若,戚戚惨惨切切')
Out[25]:
[('add', 'a', 'd'),
('all', 'a', 'l'),
('foo', 'f', 'o'),
(' zz', ' ', 'z'),
('宋甜甜', '宋', '甜'),
('范若若', '范', '若'),
(',戚戚', ',', '戚'),
('戚惨惨', '戚', '惨'),
('惨切切', '惨', '切')]
但是,这个正则是基于 python 正则匹配的特性,即:如果有分组,则所有分组结果以元组形式返回
在 c# 等支持分组的语言环境,这么写也没有问题,大不了去取第一个分组的值罢了,但是,在 js 这么写就不可行了,因为他不会返回分组的内容,而是匹配到位置了,返回一个位置信息,没有内容了!
那么 js 的正则就需要做一个大改动了。
Array.from('add and all food zzz 宋甜甜,范若若,戚戚惨惨切切'.matchAll(/(?=((.)(?!\2)(.)\3))(?=.{3})/gi)).map(x => x[1])
(9) ['add', 'all', 'foo', ' zz', '宋甜甜', '范若若', ',戚戚', '戚惨惨', '惨切切']
这里,我们用到了 es6 的一些新特性,来辅助我们获取第一个分组的内容,好在也是可以做到的
小结
现在又讲解了几个例子,结合老顾之前的几篇入门文章,相信小伙伴们已经对正则的编写有了新的体会了,那么,就放手去练习吧,只有多练多用,才会真正的掌握这些技巧。文章来源:https://www.toymoban.com/news/detail-420345.html
纸上得来终觉浅,方知此事须躬行。
文章来源地址https://www.toymoban.com/news/detail-420345.html
到了这里,关于如何才能写出一个符合预期的正则?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!