正则表达式
# 前言
很多朋友在使用自动记账的时候,直呼学习成本太高,很大一部分原因是因为规则的书写。
其中最难的两个部分莫过于正则表达式和JS(JavaScript)。
说它难,其实是因为对于“代码”有一种天然的畏惧心理。认为:
代码=编程,编程=黑客,黑客=神秘
所以,代码就约等于神秘莫测、难以理解。
其实不尽然,所谓代码、编程不过是一个按照顺序执行的流程而已,远远没有大家想的那么复杂。
更何况,正则表达式还远远算不上编程,他只是一个比较方便的文字处理工具而已。能够加快我们批量处理文字的速度,也是文字工作者必备技能。
由于这是一个入门级教学,太过复杂的东西,我在这里就不做过多的讲解。更加全面的教程,大家可以前往这里 (opens new window)继续深造。
这里是一个正则表达式测试工具:在线测试正则表达式(opens new window)
使用说明(点击展开/收起) 我这里只会讲解与自动记账相关的内容,教程中用到的例子也会围绕自动记账展开。
好,现在正式开始我们的教程:
# 什么是正则表达式
提示
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
这段文字来自 @deerchao 的正则表达式30分钟入门教程 (opens new window)。虽然这不是最权威的文献,但是作为百度搜索排第一的教程贴,仍然具有相当的参考价值。
从这段话中,我们可以提炼出以下几条信息:
笔记
正则表达式是用来处理字符串的; 什么意思?简单点说,什么是字符串?从字面意思上理解,字符串就是由文字符号组成的串。串有什么特点?串肯定是连在一起的,就像我们吃的羊肉串,每一串肯定不会在中间断成两节。那么字符串也就好理解了,就是连在一起的多个文字符号。也就是说,我们用正则表达式去找到的东西,一定是连续的。 正则表达式处理的字符串符合某些规律; 这又是什么意思?符合规律,你可能会说:世界万物,那都是有规律的。没错,但是我们这里讲的规律,没有那么广,他仅仅只是针对字符的规律,而不能够涉及数学运算。这么说可能不够明白,举个例子: 我有一大堆的数字,我要找出最后一位是2的数字,这没问题。但是你要是说要找到其中是质数的数字,那么对不起,这超出了正则表达式的能力范围,因为他涉及到了数学运算。 正则表达式仅仅是高级一点的查找/替换; 上一条其实也说过了,正则表达式不能进行数学运算。其实其他的运算也不行,比如随机数、递增数等等。 那他能干什么呢?答案就是查找/替换。相信大家都知道,查找/替换是先找到一个字符串,然后把他换掉,变成另一个字符串。而正则表达式的作用也是一样,也是先找到字符串,然后换掉它。结合前面我们讲的,那他就必然具备一个特点:替换的结果,一定是来自查找结果或者其他固定的值。这么说可能不明白,举个例子:比如我们找到两个字符串“abcd”,我们对他进行替换,就只能在abcd四个字母里面做文章,或者插进去一些固定的字符,而不能说我第一个变成abcde,第二个变成abcdf,这样是做不到的。 看到这里,很多小伙伴可能已经云里雾里,晕头转向了。没关系,智商不够可以下辈子再来。 接着往下看。
到这里,大家已经对正则表达式有了最基本的了解,接下来,我们就要开始具体的内容了:
# 开始
在书写正则规则的时候,我们可能一脸懵逼,到底要从哪里下手?下面我们来看一个例子,带着他一起走进正则的世界。
【湖南农信】您尾号0239的卡于06月09日20时47分向微信转账转出人民币400.00元,当前余额为760.12元。 这是一条银行通知短信,来自“湖南农信”。那么我们要写一条关于这条短信的规则,该怎么做呢?
🎉毫无疑问,当然得先找到它!
如果按照普通的查找,我们就可以直接将整条短信复制过去,就可以找到了。但是这样我们就满足了吗?当然不行!因为我们有很多类似的、相同格式的短信,直接复制显然是无法找到其他短信的,那么怎么办呢?
细心的你可能发现了,短信中有一部分是会变化的,有一部分是不会变化的。我们先把它们分开,把不会变化的部分单独拎出来,这样我们就得到了几个字符串:【湖南农信】您尾号、的卡于、向、转出人民币、元,当前余额为、元。。
但是这样就完了吗?当然没有。这几个字符串拼在一起,显然无法找到我们想要的结果,因为它缺少了会变化的部分。我们必须找个东西来代替这些变化的部分,这里我们暂时先用一个任意字符串.*来替代。这样我们就得到了第一个表达式:
【湖南农信】您尾号.*的卡于.*向.*转出人民币.*元,当前余额为.*元。 这是第一步,我们之后还要对这个式子进行改造。
接下来,跟着我一起,正式开始干货部分的学习:
# 1.元字符与转义
元字符——顾名思义,它是一个字符,能够代指一定范围内所有字符的代称。例如前文提到的,能够代指任意字符串的.,其实是由两个部分组成的,一个是.,第二个是。.代表的是一个任意字符,*下一节我们会讲到。下面是一些元字符的对照表:
表1-1 元字符对照表(点击展开/收起) 代码 说明 . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 \b 匹配单词的开始或结束 ^ 匹配字符串的开始 $ 匹配字符串的结束
注意
这里需要特别提到的是后面三个:\b、^、$,它们都是匹配开始和结束。但是需要注意的是:这里的开始和结束仅仅是一个标识,标明起始位置,而不是代表第一个字符。而^、$所代表的“字符串”,其实在一般情况下应当是“行”。所谓行的概念相当于自然语言中的段落,一个换行符就是一行。
元字符的问题解决了,但是新的问题来了:假如我需要搜索“^”、“$”、“.”等这些元字符本身的内容怎么办?直接写肯定不行,因为他会被解释成他代表的元字符的含义。那么怎么办呢?
答案很简单:转义
我们可以在元字符前面加上\,使其代表元字符本身。比如要匹配“^”,就在前面加\,写成^,这样就完成了转义。当然查找“\”也是一样,写成\即可。
表1-2 转义字符对照表(点击展开/收起)
# 2.重复
前文我们提到过.代表任意字符串,前一节我解释了.,这一节我们来解释。*其实就是一个重复次数的限定符,用来描述前一个字符重复匹配任意次,包括0次。所以.*就是任意字符重复任意次,也就是任意字符串。
当然,有重复任意次,也就有其他次数,请看下表:
表2-1 限定符对照表(点击展开/收起) 代码/语法 说明
- 重复零次或更多次
- 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次以上(包括n次) {n,m} 重复n到m次
到这里,我们就可以继续改造前面的例子了:
【湖南农信】您尾号0239的卡于06月09日20时47分向微信转账转出人民币400.00元,当前余额为760.12元。 我们可以观察到:尾号部分是由4个数字组成,那么我们就可以写成\d{4}。同样的,金额部分是由一串数字+“.”+两个数字组成,那么我们就可以写成\d+.\d{2}。
这样我们就得到了新的表达式:
【湖南农信】您尾号\d{4}的卡于.*向.*转出人民币\d+.\d{2}元,当前余额为\d+.\d{2}元。
# 3.捕获组
重点来了,这一步我们将会把短信中我们需要的部分提取出来。
笔记
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。
默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
这样我们就能够获取到子表达式匹配的内容了,但是我们怎样去取得它呢?只需要在替换式中写上$+组号即可,比如$1、$2。
那么下面我们来看之前的例子:
【湖南农信】您尾号\d{4}的卡于.*向.*转出人民币\d+.\d{2}元,当前余额为\d+.\d{2}元。 这是我们写好的表达式,我们现在需要提取出以下内容(仅作示例,并不与自动记账完全相同):
提示
账户名称:(格式为银行名(尾号)) 金额: 资金流向: 币种: 那么第一步,我们先把需要的内容()括起来:
【(湖南农信)】您尾号(\d{4})的卡于.向(.)转出(人民币)(\d+.\d{2})元,当前余额为\d+.\d{2}元。 第二步就是提取内容:
提示
账户名称:$1($2) 金额:$5 资金流向:$3 币种:$4 这样,就完成了。
# 4.字符类、反义
前面我们说到了元字符,我们可以轻松的找到“数字”,只要用\d就行。但是如果我需要寻找2-5之间的数字,或者13568这些数字,该怎么办呢?
方法很简单,只要把它们用中括号[]括起来就好,这个就叫做“字符类”。他能匹配到中括号内所有的字符。
值得注意的是
一个字符类仅能匹配一个字符,如果需要匹配多次,需要使用重复限定符。
而我买需要寻找除了 135 之外的所有字符呢?这时我们就需要用到反义。
字符类的反义很简单,只需要在中括号内加上一个^即可,例如[^135]。
而一些元字符的反义如\d、\w、\b、\s这些,也很简单,将字母改成大写即可。
表4-1 反义对照表(点击展开/收起) 代码/语法 说明 \W 匹配任意不是字母,数字,下划线,汉字的字符 \S 匹配任意不是空白符的字符 \D 匹配任意非数字的字符 \B 匹配不是单词开头或结束的位置 [^x] 匹配除了x以外的任意字符 [^aeiou] 匹配除了aeiou这几个字母以外的任意字符
# 5.分支条件
回到前面的例子,假如我们的银行卡中有三种货币:人民币、美元、津巴布韦币。他们的短信通知格式是一样的,那我们该怎么做呢?
这里我们就可以用到分支条件。
提示
正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。
所以我们就可以写成人民币|美元|津巴布韦币。
将这个子表达式整合进规则里面:
【(湖南农信)】您尾号(\d{4})的卡于.向(.)转出(人民币|美元|津巴布韦币)(\d+.\d{2})元,当前余额为\d+.\d{2}元。