用 python 正则表达式提取属性值
最近我简单学习 python 的正则表达式,并尝试用它来提取属性值。(1)首先什么叫做提取属性值呢?简单解释下,就是比如一个基于 HTTP 协议的网络 api 接口,会返回的响应头中比如存在 Content-Type 这个字段,它的值举例说是 "text/html; charset=utf-8",假如我们想知道在这个字符串里面的 charset 属性的值是什么?我们就可以使用正则表达式来做了。当然,有一点可以肯定,对于这个问题,写一个函数使用基于字符串的各种方法,是可以达到目的的,但是需要写的代码量也会比较多。实际上几年前我就是自己手写的这样的函数,但是现在的,我要通过 python 中的正则表达式来完成。同时,我希望把它写得更通用一点,这样我还能让它用于更多的场景中去。所以第一步我们把属性名(这里就是 “charset”)作为函数的参数来传入,假设这个参数叫做 strKey。假如我们需要搜索的是 “charset” 的值,那么正则表达式的 patter 字符串就可以写成(为了方面展示,下面的字符串都不带两边的表示字符串的引号,也就是说下面显示的都是字符串值被 print 出来的字面值效果):确实就是这么简单,HTTP 协议里也规定了,charset 是一个 token,所以它不会被引号括起来,但是为了用于更广的场景,我们让 strKey 可以被引号括起来,应该怎么写呢,这里我试了半天,发现最佳写法还是这样:([^"\w]charset|"charset")\s*=\s*这样就可以同时匹配 'charset=xxx' 和 '"charset"=xxx" 这样的字符串,但是不会匹配 'mycharset=xxx' 这样的字符串。也就是说 key 的两边有没有引号都可以,都能匹配。同时等于号两边,也可以加任意数量空格(虽然 HTTP 协议规定在 Content-Type 的 params 中,等号两边不能加空格)。此外还要 charset 出现在字符串的起始位置,所以变成:(^charset|[^"\w]charset|"charset")\s*=\s*这样,'key = ' 这个部分就写好了,这个 key 可以被 quoted,也可以不被 quoted。现在我们考虑 'key=' 后面的 value 的部分。 在 HTTP 协议中,规定了 params 的 value 可以是一个 token,或者是一个 quoted string。在 HTTP 协议中的 quoted string 简单说就是两边有引号字符包裹的一个字符串,而且和编程语言中的那种字符串字面值定义不同的是,HTTP 协议中的 quoted string 中中间的那部分是不能包含双引号字符的,这应该是为了解析和解释的简便。这一点不同于编程语言里的字符串常量,是可以通过转义字符,把引号字符也放在字符串中的。实际上这样也为写正则表达式提供了方便性,如果是编程语言中的那种字符串,里面可以包含任何转义字符,这个特性过于灵活,用正则表达式恐怕是几乎没法写的。所以这里只需要考虑 HTTP 协议中规定的简化版 quoted string 就可以了。这里使用到 python 正则表达式中的特性,就是可以通过判断是否存在某个 group 的匹配,来进行分支判断的语法:(^charset|[^"\w]charset|"charset")\s*=\s*(["])?(?(2)([^"]+)\2|([^()<>@.;:\\"/[\]?={}\s]+))这样就可以同时匹配 'charset=utf-8' 和 ‘charset="utf-8"’ 这两种写法了,也就是说 value 部分可以是一个 token(不带引号),可以是一个 quoted string(两边带双引号字符)。除了整体匹配的 group0, 这里一共有 4 个 group,第一个 group 表示 'charset' 部分,第二个 group 是 value 前面的引号字符,第三个 group 是 value 为 quoted string 时脱去两边引号的部分,第四个 group 是 value 为 token 时的值。所以返回属性值的时候,返回 match.group(3) or match.group(4) 就可以了。