使用C#从驻留在DB中的字符串集自动生成Regex

时间:2010-05-28 16:57:23

标签: c# regex

我在数据库中有大约100,000个字符串,我想如果有办法从这些字符串中自动生成正则表达式模式。所有这些都是字母字符串,并使用英文字母组成的字母表。例如,不使用(X,W,V)。是否有任何函数或库可以帮助我在C#中实现此目标?示例字符串是

KHTK
RAZ

鉴于这两个字符串,我的目标是生成一个正则表达式,允许像(k,kh,kht,khtk,r,ra,raz)这样的模式当然不区分大小写。我已经下载并使用了一些有助于生成正则表达式的C#应用​​程序,但这在我的场景中没有用,因为我想要一个进程,在这个进程中我顺序从db读取字符串并将规则添加到regex,这样这个正则表达式可以在以后的应用程序中重用或者保存在磁盘上。

我是正则表达式模式的新手,不知道我问的问题是否可能。如果不可能,请建议我一些替代方法。

1 个答案:

答案 0 :(得分:2)

一种简单的(有些人可能会说天真)方法是创建一个连接所有搜索字符串的正则表达式模式,由交替运算符|分隔:

  1. 对于您的示例字符串,可以获得KHTK|RAZ
  2. 要获得正则表达式捕获前缀,我们会在模式中包含这些前缀,例如: K|KH|KHT|KHTK|R|RA|RAZ
  3. 最后,为了确保这些字符串仅被整体捕获,而不是作为较大字符串的一部分,我们将匹配行首和行尾操作符以及每个字符串的开头和结尾,分别为:^K$|^KH$|^KHT$|^KHTK$|^R$|^RA$|^RAZ$
  4. 我们希望Regex类实现能够将长正则表达式模式字符串转换为高效的匹配器。

    此处的示例程序生成10,000个随机字符串,以及与这些字符串及其所有前缀完全匹配的正则表达式。然后程序验证正则表达式确实只匹配那些字符串,并计算所需的时间。

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace ConsoleApplication
    {
        class Program
        {
            private static Random r = new Random();
    
            // Create a string with randomly chosen letters, of a randomly chosen
            // length between the given min and max.
            private static string RandomString(int minLength, int maxLength)
            {
                StringBuilder b = new StringBuilder();
    
                int length = r.Next(minLength, maxLength);
                for (int i = 0; i < length; ++i)
                {
                    b.Append(Convert.ToChar(65 + r.Next(26)));
                }
    
                return b.ToString();
            }
    
            static void Main(string[] args)
            {
                int             stringCount = 10000;                    // number of random strings to generate
                StringBuilder   pattern     = new StringBuilder();      // our regular expression under construction
                HashSet<String> strings     = new HashSet<string>();    // a set of the random strings (and their
                                                                        // prefixes) we created, for verifying the
                                                                        // regex correctness
    
                // generate random strings, track their prefixes in the set,
                // and add their prefixes to our regular expression
                for (int i = 0; i < stringCount; ++i)
                {
                    // make a random string, 2-5 chars long
                    string nextString = RandomString(2, 5);
    
                    // for each prefix of the random string...
                    for (int prefixLength = 1; prefixLength <= nextString.Length; ++prefixLength)
                    {
                        string prefix = nextString.Substring(0, prefixLength);
    
                        // ...add it to both the set and our regular expression pattern
                        if (!strings.Contains(prefix))
                        {
                            strings.Add(prefix);
                            pattern.Append(((pattern.Length > 0) ? "|" : "") + "^" + prefix + "$");
                        }
                    }
                }
    
                // create a regex from the pattern (and time how long that takes)
                DateTime regexCreationStartTime = DateTime.Now;
                Regex r = new Regex(pattern.ToString());
                DateTime regexCreationEndTime = DateTime.Now;
    
                // make sure our regex correcly matches all the strings, and their
                // prefixes (and time how long that takes as well)
                DateTime matchStartTime = DateTime.Now;
                foreach (string s in strings)
                {
                    if (!r.IsMatch(s))
                    {
                        Console.WriteLine("uh oh!");
                    }
                }
                DateTime matchEndTime = DateTime.Now;
    
                // generate some new random strings, and verify that the regex
                // indeed does not match the ones it's not supposed to.
                for (int i = 0; i < 1000; ++i)
                {
                    string s = RandomString(2, 5);
    
                    if (!strings.Contains(s) && r.IsMatch(s))
                    {
                        Console.WriteLine("uh oh!");
                    }
                }
    
                Console.WriteLine("Regex create time: {0} millisec", (regexCreationEndTime - regexCreationStartTime).TotalMilliseconds);
                Console.WriteLine("Average match time: {0} millisec", (matchEndTime - matchStartTime).TotalMilliseconds / stringCount);
    
                Console.ReadLine();
            }
        }
    }
    

    在Intel Core2盒子上,我得到10,000个字符串的以下数字:

    Regex create time: 46 millisec
    Average match time: 0.3222 millisec
    

    当将字符串数量增加10倍(达到100,000)时,我得到了:

    Regex create time: 288 millisec
    Average match time: 1.25577 millisec
    

    这个更高,但增长率低于线性。

    应用程序的内存消耗(10,000个字符串)从~9MB开始,最高达到~23MB,必须包括正则表达式和字符串集,并且最后降到~16MB(垃圾收集开始?)绘制你的从中得出结论 - 该程序没有优化来从其他数据结构中挑出正则表达式内存消耗。

相关问题