Bingo, Computer Graphics & Game Developer

初尝正则表达式

基本上初步入门都是在这里正则表达式入门教程学到的,当然没有30分钟那么夸张。

许多时候如若不进行实际测试很难知道自己的正则表达式正确与否,一般我就在这里正则表达式在线测试或者Rebular直接在线测试,其实道理都一样。

从开始学习,到实际能够解读正则表达式,到真正运用正则表达式在CSS文件的解析上,我起码花了整整三天时间,甚至更多

首先说说我自己的感想,正则表达式难度不在于其各个元字符的搭配很难,而是解析的这一步很难。所以我自己结合了教程总结了如下经验。

案例1

^(\*?[-#/\*\w]+(\[[0-9a-z_-]+\])?)\s*
乍一看非常难以分解,不过我一般会这样做

^                           #字符串开始
(
      \*?                     #可选的*
      [-#/\*\w]+              #"-""#""\""*""字母数字下划线或汉字"出现一次或多次
      (
          \[[0-9a-z_-]+\]         #"["+0~9|a~z|_~空格+"]"
      )?                      #上述字符串是可选的
)
\s*                         #任意多空格

上述代码不会对正则表达式产生任何的影响,因为(?= #xxx)内部的内容表明为注释,这样的缩进可以更明显的看出来内容,我个人更偏向于下面这样的用法。

// ^                    字符串开始
// (
//     \*?                  可选的任意出现零次或者多次的字符
//     [-#/\*\w]+           "-""#""\""*""字母数字下划线或汉字"出现一次或多次
//     (\[[0-9a-z_-]+\])?   可选的"["+0~9|a~z|_~空格+"]"
// )
// \s*                  任意多空格
static const std::regex _RE0_ = RE(R"raw(^(\*?[-#/\*\w]+(\[[0-9a-z_-]+\])?)\s*)raw");

这样就有一个好处,在实际使用此正则表达式时,不用在表达式内部修改样式,有的OS例如Windows,会要求在每行的最后加上\类似于这样

string test2 = 
"body{\
    background-color: #d0e4fe;\
}\
\
h1{\
    color: orange;\
    text-align: center;\
}\
\
p{\
    font-family: \"Times New Roman\";\
    font-size: 20px;\
}";

这对有强迫症的人来说简直是致命的,但又没有什么好的办法。所以我个人偏向于将正则表达式的注释写到对应语言的注释中。

对于元字符其实没有什么太多能够讲述的

反义

代码 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

重复

代码 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

要说说的就是这个反义,^这个字符如若不在[]中,那么它的意思一般就是表示从字符串开始的位置,当然如果前面没有转义字符的话。一旦跑到了[]中,那么它就代表了反义。

案例2

^([^\{]+)

// ^        字符串开始处
// (
//       [^\{]+   除{以外其他的一个或者多个字符
// )

这里的两个^就展现了不同的含义,一开始我在这里费了不少功夫。

删除注释

有的正则表达式可以匹配得到多门语言当中各型各色的注释,以便于直接找到删除。删除注释大多数的语言注释的正则表达式都可以在这里找到。C/C++/Java因为语言相通所以注释匹配也是可以通用的。

在实际应用中,我使用/\*[^\*]*\*+([^/\*][^\*]*\*+)*/来匹配CSS的注释,读者可以自行测试。

在实际的CSS解析中最难以理解的表达式如下
^((?:'(?:\\'|.)*?'|\"(?:\\\"|.)*?\"|\([^\)]*?\)|[^\};])+)

// ^
// (
//    (
//        ?:            可选的:
//        '(?:\\'|.)*?'             单引号围起来的内容或者任意字符 若前方有',那么需要以'结尾
//        |
//        \"(?:\\\"|.)*?\"          双引号围起来的内容或者任意字符 若前方有",那么需要以"结尾
//        |
//        \([^\)]*?\)               (开始 匹配除了)以外的任意字符 字符可以出现零次或者多次[^x] 可选的)
//        |
//        [^\};]                    匹配除了} ;以外的任意字符
//    )
//    +                 可以有零个或者多个上述值出现
// )

这里的单引号的确是难以理解,后来才发现其实单引号不需要转义就可以直接表达自身的意思,可以对比下一行的双引号。因为有的CSS property: value;里的value是字符串/单一字母/xxx(xxx)/等等形式 如

background-color: rgb(0, 10, 20); 

background-color: #123456;

background-color: red;

很种类繁多,因此需要一并写出。