多正则表达式匹配工具 的用法

Posted Terark-CTO-雷鹏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多正则表达式匹配工具 的用法相关的知识,希望对你有一定的参考价值。

请关注我们的技术创业项目 Terark,领先的数据技术提供商


2014年5月4日09:03从 http://code.google.com/p/febird/wiki/MultiRegexMatch 更新至最新版

Introduction

This Multiple Regex Matching solution includes two parts:

  • An offline multiple regex builder application: regex_builder
    1. regex_builder build multiple regex into a DFA file
    2. if using binary mode, generate an optional binmeta text file describe the regex meta info

  • An online multiple regex matching API
    1. Use the general API load the DFA built by regex_builder
    2. Construct the MultiRegexFullMatch object from the DFA
    3. Construct the MultiRegexSubMatch, if the submatch capture is required
      • Note: This algorithm could only capture submatch for one-pass regex
  • This algorithm is very efficient for large number(such as 1,000,000) of regex recognition, a shinning example is query classification
  • This algorithm could be regarded as a generic Key-Value map, which Key is a regular expression

介绍

这个程序包含一个非常高效的算法,用来匹配多个正则表达式。经过预处理,仅用 O(n) 的时间复杂度,就可以识别出一个输入字符串(长度为n)能匹配哪些(可能是多个)正则表达式。算法的详细内容可参见:

  1. 多正则表达式匹配(Multiple Regular Expression Matching)
  2. 多正则表达式匹配(Multiple Regular Expression Matching) 中的动态 DFA 算法

作为一个完整的解决方案,这个程序包括两部分:

  • 一个离线 Build 程序:regex_build
    1. regex_build 会把输入的正则表达式编译成一个 DFA ,正则表达式的编译使用了 re2 的前端 Parser
    2. 如果使用二进制模式,还会生成一个 binmeta 文本文件,用来描述 DFA 的元信息
      • 该文件生成后不可被更改,否则会产生未定义行为
      • 目前应该首选二进制模式

  • 一个动态库,提供匹配接口
    1. 从 (regex_build生成的) DFA 文件 和 binmeta(二进制模式) 文件 加载 并 创建出 用来执行匹配 的 对象
    2. 在二进制模式下,构造 MultiRegexFullMatch 对象执行全匹配,此时不获取 !Submatch,仅返回能匹配上的正则表达式 ID:MultiRegexFullMatch示例程序
    3. 在二进制模式下,构造 MultiRegexSubmatch 对象执行匹配:MultiRegexSubmatch示例程序
      • MultiRegexSubmatch 仅能获取 one-pass 正则的 submatch
      • one-pass 正则的判断直接调用了 re2 的判断函数 IsOnePass
        • 很不幸,该函数会将正则表达式 从([^到]+)到([^怎]+)怎么走 判为非 one-pass,但实际上,在 unicode 字符集内,它的确是 one-pass,只是 re2 的底层引擎是基于字节的,所以它认不出来
        • 如果你愿意冒险,regex_build 给你一个选择,将所有的正则表达式标为 one-pass,MultiRegexSubmatch::match_utf8 在搜索完之后,会按 utf8 编码规则做一个边界修正,就可以正确提取出 从([^到]+)到([^怎]+)怎么走 中的 submatch 了
  • 该程序对大规模的规则系统(例如100万个正则表达式)会非常有用,比如query分类

Compile

$ cd febird-trunk
$ make -C tools/regex
$ ll tools/regex/*/*.exe
-rwxrwxrwx 1 leipeng leipeng 15M 2013-11-02 15:41:54 tools/regex/dbg/regex_build.exe
-rwxrwxrwx 1 leipeng leipeng 26M 2013-11-02 15:42:06 tools/regex/rls/regex_build.exe

regex_builder 使用方法

regex_builder.exe 将很多个正则表达式 offline build成一个DFA文件,online程序使用时,先加载DFA文件,当匹配文本时,可以获知匹配到了哪些正则表达式,同一个文本可能匹配多个正则表达式。

匹配接口分文本接口于二进制接口两种,目前二进制接口已经有了很友好的封装,推荐使用。

文本接口的使用方法与之前的DFA词表完全相同(match_key接口)。

该程序使用 re2 的parser前端,生成 febird 自己的 DFA 文件

命令行选项与参数

命令行: regex_build.exe Options

Options 命令行选项 说明
-i Regex.txt 输入的正则表达式描述文件,也可以通过标准输入传递,该参数优先于标准输入
-O regex_dfa_file 生成的自动机文件
-a 从所有位置开始匹配,相当于在所有正则表达式之前加 .* ,这会加快匹配速度,因为不需要重新从输入文本的每个位置开始搜索,
但会大大增加内存用量(20倍以上),build消耗的时间也会显著增加
-A 在所有正则表达式合并之后加 .* ,仅用于测试
-b binmeta_file 生成 binmeta_file,是在 Binary 模式下获取 Submatch 时使用的元信息,使用二进制匹配接口时,必须加这个选项
-g 为每个正则表达式生成三个 dot 文件,该文件用来可视化正则表达式自动机的状态图NFA/DFA/MinimizedDFA
-G 生成整个DFA的dot文件,通常情况下,该文件会很大
-L 不使用UTF8,使用Latin1;不加该选项时(默认情况)使用的是UTF8
-d delimiter 将正则表达式看做Key,delimiter表示key,value之间的分隔符, build时会将该字符从正则表达式的DFA中删除,
因此目标文本中出现此字符时,匹配就会失败。默认是 256,在 byte 取值范围之外
-c conflict_report_file 当同一个文本会被多个正则表达式匹配时,此时称为冲突,该选项将冲突的正则表达式的id写到conflict_report_file
很多时候冲突是不可避免的,但是,根据 confict_report_file,可以修改正则表达式,尽可能减小冲突的可能性
-s 捕获 submatch,也就是 () 中的部分,默认情况下不捕获 submatch
-t dfa_type 默认dfa_type=d, 表示 DenseDFA,专为正则表达式优化的DFA
d以外的其它字符,表示DFA类型为一般DFA,主要为词表DFA优化
-D 构建动态DFA以节省内存和构建时间,在某些情况下,构建完整DFA甚至是不可能的,
如果没指定该选项,该程序会尝试用100倍于所有正则表达式的内存,如果失败,仍然会构建动态DFA
-I 正则表达式忽略大小写

关于 -d 选项

一开始 febird DFA 通过 match_key 接口来实现正则表达式匹配,必须在 byte 的取值范围 [0, 256) 之间取一个作为分隔符。后来,经过仔细考虑,通过扩充自动机的字符集( r1303),从而 delimeter 可以在 [0, 256) 之外取值,于是就不再需要从 [0, 256) 取一个特殊值来作为分隔符。
这样,正则表达式匹配就可以有更广的适用范围。另一方面,表面上看,似乎去除一个特殊byte值作为delimeter会影响二进制模式的匹配,其实一点也不会。正则表达式相当于key,正则表达式的 id 相当于是 value,delimeter 不能出现在 key 中,但可以出现在 value 中,从而,value可以是任意二进制数据。实际上,二进制匹配接口的实现是先于  r1303 的。
于是,现在,只有在当  你知道你在干什么 的时候,才需要指定  -d delim 选项,否则,一定不要用 -d

输入文件Regex.txt 的格式

regexp \\t id

第一列是正则表达式,如果行首字符是 *,表示忽略该正则表达式的 one pass 属性,无条件获取 submatch,用 * 做标志的原因是以 * 开头的正则表达式是非法的正则,从而不会引发歧义,也不会减少表达能力。

第二列是正则表达式的id,该id可以是任意字符串,用来标识这条规则。多个正则表达式可以有相同的id,此时等效于将多个正则表达式或起来放在一行。

一个示例的Regex.txt

a.*b    a-dot-star-b
a[a-f]*b    1
a[a-f]*([a-c]*)+[bc]+   2
a(1|2|3|4)[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab]5   3
a([0-3a-dx-z]2,)[0-2cdxy]*abc 4
(1[358][0-9])[0-9]8   mobile-phone-num
\\+86-(1[358][0-9])[0-9]8  china-mobile-phone-num
\\d3\\.\\d3\\.\\d3\\.\\d3  ip_address
antidisestablishmentarianism    一般人能看懂的最长单词

非常重要!注意事项!

谨慎使用 .* ,特别是当 .* 处于正则表达式开头时

普通字符串(不包括正则表达式的元字符)也可以放进Regex.txt,可以为所有普通字符串赋予一个相同的id,这样就将正则表达式和普通字符串build到同一个DFA中了,普通字符串在DFA中占的空间相对要小得多,如果普通字符串很多,可以尝试用 -t x选项,看生成的DFA文件是否更小。

匹配接口: 二进制模式

要使用二进制接口,在使用 regex_build.exe 创建自动机时,必须加 -b bin_meta_file 选项

MultiRegexFullMatch

参考 MultiRegexFullMatch示例程序

MultiRegexSubMatch

参考 MultiRegexSubmatch示例程序

匹配任意位置

如果要匹配任意位置,需要自己每次前进一个(utf8)字符,重新开始匹配(match_and_print):

 #include <febird/util/unicode_iterator.hpp> // for febird::utf8_byte_count
 // ......
 // ...
   fstring text = some string;
   for (size_t off = 0; off < text.size(); ) 
     fstring suffix(text.begin() + off, text.end());
     match_and_print(sub, suffix);
     off += febird::utf8_byte_count(text[off]);
   

匹配接口: 文本模式

文本接口是在创建该算法的原型时使用的,不推荐使用,除非作为 Demo 或者——你知道你在干什么!

匹配接口使用方法可以参考 DFA_Interface::match_key 示例程序

直接去 febird-trunk/samples/automata/abstract_api/ 目录运行 make即可编译,编译输出在 rls 和 dbg 目录下

编译出来的 match_key 程序可用来测验匹配(match_key程序使用的 delimiter是 \\t ):

febird-trunk/samples/automata/abstract_api/rls/match_key -d -i samples.dfa abcccb 输出:

abcccb ----------
ab                   value: idx=00000000 str=1
ab                   value: idx=00000001 str=2
ab                   value: idx=00000002 str=a-dot-star-b
abc                  value: idx=00000000 str=2
abcc                 value: idx=00000000 str=2
abccc                value: idx=00000000 str=2
abcccb               value: idx=00000000 str=1
abcccb               value: idx=00000001 str=2
abcccb               value: idx=00000002 str=a-dot-star-b

输出的 str=1 str=2 str= a-dot-star-b就是匹配到了id为1、2、a-dot-star-b的正则表达式

使用 match_key 接口,正则表达式匹配和词典匹配就完全相同。


以上是关于多正则表达式匹配工具 的用法的主要内容,如果未能解决你的问题,请参考以下文章

grepegrep及相应的正则表达式和用法

三个支持正则表达式的行处理的工具: grep/sed/awk

mysql数据库中 正则表达式 “n,m”的用法?

Python初学者

正则表达式知识补漏

grep用法