正则 匹配 回车换行_丁哥卯妹聊编程之正则表达式

物介绍:

b8e11be02b41a7889d43272970a06999.png 大家好,我是活泼可爱的卯妹,是银行从事基础技术研究的新人,我爱学习,不怕难,以后请多多指教! b750f214b6a75118c925fb9dd5d0011e.png 大家好,我就是传说中的丁哥,是银行从事基础技术研究的专家,喜欢技术钻研,也喜欢健身,看我的胳膊!   注:感谢浙江理工大学范宜蕾同学友情绘制卡通形象
(场景)
中午,吃饭回来的丁哥看到卯妹仍在愁眉苦脸地盯着屏幕,于是走上前去询问。 d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png 丁哥:“卯妹,还不去吃饭在忙什么呢?”

卯妹:“丁哥,我正在想怎么从这段文字中提取数据呢?”

Tasks:114 total,  1 running,  113 sleeping, 0 stopped,  0 zombie

%Cpu(s):  0.0 us, 11.8 sy,  0.0 ni, 88.2 id, 0.0 wa, 0.0 si, 0.0 st

KiBMem:  1863224 total,  1504272 free,   156796 used, 202156 buff/cache

KiBSwap:  2097148 total,  2097148 free,        0 used, 1514832 avail Me

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

丁哥看了看屏幕,原来卯妹是要从top命令的输出中提取出各个内存使用数据,于是问道:“你想怎么做呢?”

卯妹: “我想先跳过2行,然后在第3行查找第一个冒号位置,再找到total位置,再……” 丁哥: “打住!你不会是想按照关键字把每个位置找出来然后再截取字符串吧?” 卯妹 有点不好意思地说:“是啊,就是我觉得有点笨……”
丁哥
毫不客气地说:“可不是!我来问你一个问题,如果前面某行数据或者第3行数据由于某些原因中间换行了咋办?”
卯妹
立刻答道:“那就出错了!但是我们拿到的输出结果不可能是这个样子……”
“停!” 丁哥 打断了卯妹道:“谁能保证拿到的数据一定规范?所以,我们的程序必须要考虑这些情况,要足够健壮!”
卯妹
适时地说道:“那丁哥你教教我呗,你肯定有招儿!”
“好。” 丁哥 点了点头:“其实最适合提取数据的方法是利用正则表达式!”
卯妹
不好意思地挠了挠头:“我以前好像见过,觉得麻烦没学……”“麻烦?”丁哥反问道:“其实正则表达式非常简单,就是一套符号语言而已,它用某些特定的符号和规则来描述文本的模式,然后去处理这个模式,如此而已。就拿你这个例子来说,可以很简单地写出它的模式来。”丁哥一边说着,一边在键盘上飞快地敲下了一行文字。

Mem:\s*(\d+)\s*total,\s*(\d+)\s*free,\s*(\d+)\s*used,\s*(\d+)\s*.*

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

卯妹苦笑了一下说:“丁哥,你这是天书吧,哪里简单了?

丁哥摆了摆手:“看正则表达式不能一下子看整体,你要拆解开来看。再长的表达式也是由一系列基本元素组成的,明白了基本元素就简单啦!”

卯妹“那你赶紧教教我吧!”

丁哥“你先看看我写的这个模式串跟你原来要提取数据的字符串有什么相同的地方不?”

卯妹看了看说:“Mem、total等这些单词跟原来完全一样。”

丁哥“是的。一般来说,在正则表达式里,除了一些特殊符号,每个字符还是它本来的含义。我们把一些字符按照原样写在表达式里,就是为了能准确匹配或者定位一些数据。”

卯妹拍了拍脑袋:“你把Mem、total等写在这里,就跟我定位他们位置截取一个道理!”

丁哥“嗯!现在你再看一下,这里面是不是有好几个 . \s\d+*。”

卯妹“是啊,这些是什么意思呢?”

丁哥”在正则表达式里他们是有特殊意义的。其中句点 .可以代表任何一个字符,\s代表空白字符如空格、TAB、回车换行符等等,\d代表0-9之间的任意数字,而+则代表它前面的字符可以出现1次或多次,*代表它前面的字符可以出现0次或多次。”

卯妹立刻道:“哦,我明白了!那么 .* 就代表可以是任意一个字符出现0次或多次呗,就是说它啥都能匹配,\d+就是表示可以出现多个数字,所以用来代表里面的数字部分,\s*代表可以有0个或多个空格,就是用来代表里面的空白部分的!”

丁哥“基本正确,不过关于 .* 你说得不准确,在不特意指定的情况下它不能代表回车换行符,这个先不说,你先再看一下这段代码中还有什么是我们刚才没提到的?”

卯妹重新看了看代码说:“好像就只有几个小括号了,他们是什么意思啊?”

丁哥“这还用我说,你本来想干啥来着?”

卯妹“啊”了一声不好意思地说:“提取数据,那它们的意思就是把括号的数据提取出来了!”

丁哥“是的,每组括号对于正则表达式就是一个捕获组的概念,它们会按照左括号出现的先后顺序进行编号,后面你就可以根据组号把数据提取出来啦。”

卯妹“丁哥,那这个该怎么用啊?”

丁哥“这个简单,Java里几行代码就行,你看……”丁哥说着又飞快地敲起了代码。

Pattern mem = Pattern.compile("Mem:\\s*(\\d+)\\s*total,\\s*(\\d+)\\s*free,\\s*(\\d+)\\s*used,\\s*(\\d+)\\s*.*");

Matcher mr = mem.matcher(topString); //topString 此处代指top命令的输出,略。

if(mr.find()){for(int i=1;i<=mx.groupCount();i++)
    System.out.println(mx.group(i));
}

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

敲完之后丁哥点了一下运行按钮,控制台上立刻依次输出了四个内存使用数字。

卯妹拍了一下手说:“丁哥厉害啊!”但转念一想又道:“那你刚才提到的,如果前面不止2行有多行咋办?还有如果第3行数据中间插入了换行咋办?”

丁哥笑了笑道:“先说第一个问题,你看的表达式里有写第几行么?”

卯妹“没有”。

丁哥立刻在测试文本前面加了几个空行,然后又执行了一下程序,输出结果依然正确无误。

丁哥继续说道:“你看,由于正则表达式是模式匹配,跟行数无关的,所以不管前面几行都OK。现在我们来看看第二个问题。”丁哥说着在要提取数据的这一行中间的free,之后加入了一个回车,然后再一次运行程序,这一次程序的运行结果依然正确。

看着卯妹一脸兴奋的样子,丁哥不失时机地说:“你看,一样能兼容吧!”

卯妹道:“是啊!真是太强大了!”

“强大你个头!”丁哥轻轻地拍了卯妹一下:“你再看我这样修改一下。”丁哥说着又在free这个单词中间两个e的前面加入了一个回车,再一次运行程序居然什么结果都没有输出!

卯妹目瞪口呆地问道:“这是为什么啊?怎么还跟位置有关?”

丁哥微微一笑说:“第一次我加入回车,是跟空格紧紧挨着,因为\s能够匹配回车,所以没问题。第二次我插入到了free中间,而我们模式要求free中间不能有任何东西,自然不能匹配了!”

卯妹恍然大悟道:“原来是这样!”

丁哥继续道:“所以说,再使用正则表达式式,必须认真地思考、仔细地测试,稍有不慎就有可能给自己挖坑。”

卯妹点了点头道:“谢谢丁哥!可是,这个问题怎么解决呢?”

丁哥道:“这个时候,就需要我们修改一下表达式,写出一个更合适的来了。同时也正好提醒你一下,对于同一个问题,可能有多种正则表达式方案,不是唯一的哦。”丁哥说着,写下了他的第二个表达式,然后示意卯妹先自己分析一下。

Mem :\s*(\d+).*?\s*(\d+).*?\s*(\d+).*?\s*(\d+).*? 

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

过了一会儿,卯妹说道:“这个表达式跟之前的主要区别就是原来的每一处数字后面的\\s*total之类的替换为了.*?,这个是什么意思呢?”

丁哥“观察力不错嘛!.可以代表任意字符自然也可以代表空格了,所以这里替换没有什么问题。但是之前说过它未必能代表回车换行,而这正是我们的问题所在。所以需要在使用时通过选项(Pattern.DOTALL)指定句点可以代表包括回车换行在内的任意字符。另外,正则表达式在匹配时缺省是以一行为单位的,所以我们还需要指定为多行模式(Pattern.MULTILINE),即将多行视为一个整体进行匹配,就可以了。”

“另外”丁哥继续说:“这里的代表非贪婪模式。什么意思呢,缺省情况.* 会能尽量多的匹配,能匹配多少就匹配多少,这叫贪婪模式,而如果在后面加一个 ? 就表示它会尽量少的匹配,也就是它会考虑到后面的匹配,不至于让一个 .* 匹配了所有。在我们这个例子里,如果不指定这个,那么第一个 .* 就会把第一个数字后的所有内容都匹配完了,也就导致我们抓不到数据啦。当然了,这个也可以用在\d\s[a-z]这样的任何其它模式之后。

说完之后,丁哥修改并运行了一下代码,果然控制台上又输出了提取出来的四个数字。

Pattern mem = Pattern.compile("Mem :\\s*(\\d+).*?\\s*(\\d+).*?\\s*(\\d+).*?\\s*(\\d+).*?",

Pattern.DOTALL |Pattern .MULTILINE);

Matcher mr = mem.matcher(topString); //topString 此处代指top命令的输出,略。

if(mr.find()){
    for(int i=1;i<=mx.groupCount();i++)
    System.out.println(mx.group(i));
}

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

卯妹拍了拍手道:“现在这个表达式比之前看起来更简洁了!不过我发现里面还有重复的地方,比如说\s*(\d+).*?这一段就出现了四次,能不能再简化一下呢?”

丁哥哈哈一笑:“孺子可教也!当然可以,正则表达式里可以用{m,n}这样的格式来指定前面的模式出现次数介于mn之前,如果只指定{m}这种形式就是一定出现m次,比如o{3}就代表ooo,再比如(a\d+b){2}就可以代表a12ba3b,等等,所以我们的表达式可以这样改……”

“我知道了!”不等丁哥说完卯妹就迫不及待地写下了第三个式子然后以期待的目光看着丁哥。

Mem :(\s*(\d+).*?\s*){4}

d1d486efb08c5b3b0e410e6421b4ffc4.png 77fda0eae57c182149aad5c03b4309bb.png

丁哥不禁笑了一笑道:“看我干啥?对不对你自己运行一下不就知道了。”

卯妹立刻点了一下运行按钮,出乎意料之外的这次却只输出了两个结果,其中第一个结果是202156以及它前面的多个空格,第二个结果是202156这个数字,也就是要提取的四个数字之中的最后一个。

看着一头雾水的卯妹,丁哥不无得意地说道:“正则表达式也是有坑的,比如Java的正则表达式是不允许在编译表达式时捕获组个数的。像你这个表达式,里面只有两组括号,也就是两个捕获组。但是你指定了这个模式要出现四次,所以它虽然匹配到了正确的数据,最终却只能用最后一组数据覆盖这两个捕获组,也就是现在这个输出的原因。”

“哦,原来是这样!”卯妹恍然大悟道:“那岂不是就不能这样用了?”

丁哥立刻反诘道:“怎么会没用!难道你用正则表达式就一定要提取数据吗?”

望着还没回过神来的卯妹,丁哥一边说一边站了起来:“好了,剩下的交给你了!”

“丁哥,别走啊,你再教教我呗!”卯妹一着急赶紧站了起来,差点打翻一旁的肥宅快乐水。

“师傅领进门,修行靠个人,正则表达式的文章一搜一大把,我就是给你引引路,剩下的靠你自己啦!”丁哥说完哈哈一笑走了开去。

(完)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值