【问题标题】:Large RegEx Match causing program hang大型正则表达式匹配导致程序挂起
【发布时间】:2012-05-21 15:26:12
【问题描述】:

前几天我试着问这个问题,但诚然一开始没有很好地表达问题或发布代码,答案被关闭了。所以我在这里再次尝试,因为老实说,这让我很快发疯。 :)

我正在尝试实现这个Address Parser,它最初是一个基于控制台的c# 程序。我已经成功地将它转换成一个独立的 WPF 程序,它只包含一个用于输入的 TextBox,一个用于激活解析的 Button,以及一个用于显示结果的 TextBlock。在写这篇文章时,我确实将输出截断为我在主程序中需要的内容,但它仍然可以正常工作。我已经在下面包含了整个代码。

我的下一步是将其移植到我的主程序中,我通过字面上使用复制/粘贴来完成。但是,在运行此程序时,程序会在按下按钮后挂起。最终 VS 给出了一个错误,即进程运行时间过长而没有输出消息,并且 TaskManager 中的内存使用量逐渐从 ~70k 增加到 3,000,000。针对这种情况,我将Parsing方法分配给了一个后台worker,希望能减轻主进程的工作量。这确实解决了程序冻结的问题,但后台线程只是做了同样的事情,提高了 RAM 使用率并且什么也不返回。

所以现在我陷入了僵局。我知道问题出在var result = parser.ParseAddress(input); 语句的某个地方,因为当对每一行代码使用断点时,这是最后一个触发的。但基本上我不明白为什么这会在一个 WPF 程序而不是另一个程序中导致问题。

如果有必要,我会非常乐意将主程序的完整源代码发布到某个地方,但我无法想象在这里发布大约 20 个不同的类文件和项目的代码会是一个好主意。 :)

独立的 WPF 应用程序

namespace AddressParseWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public void Execute()
        {
            AddressParser.AddressParser parser = new AddressParser.AddressParser();
            var input = inputTextBox.Text;

            var result = parser.ParseAddress(input);

            if (result == null)
            {
                outputTextBlock.Text = "ERROR. Input could not be parsed.";
            }
            else
            {
                outputTextBlock.Text = (result.StreetLine + ", " + result.City + ", " + result.State + "  " + result.Zip);
            }
        }

        private void actionButton_Click(object sender, RoutedEventArgs e)
        {
            Execute();
        }
    }
}

Parser嫁接主程序

public void ExecuteAddressParse()
{
    AddressParser.AddressParser parser = new AddressParser.AddressParser();
    var input = inputTextBox.Text;

    var result = parser.ParseAddress(input);

    if (result == null)
    {
        outputTextBlock.Text = "ERROR. Input could not be parsed.";
    }
    else
    {
        outputTextBlock.Text = (result.StreetLine + ", " + result.City + ", " + result.State + "  " + result.Zip);
    }
}       

private void actionButton_Click(object sender, RoutedEventArgs e)
{
    ExecuteAddressParse();
}

ParseAddress 方法

public AddressParseResult ParseAddress(string input)
{
    if (!string.IsNullOrWhiteSpace(input))
    {
        var match = addressRegex.Match(input.ToUpperInvariant());
        if (match.Success)
        {
            var extracted = GetApplicableFields(match);
            return new AddressParseResult(Normalize(extracted));
        }
    }

    return null;
}

正则表达式匹配方法

private static void InitializeRegex()
{
    var suffixPattern = new Regex(
        string.Join(
            "|",
            new [] {
                string.Join("|", suffixes.Keys), 
                string.Join("|", suffixes.Values.Distinct())
            }),
        RegexOptions.Compiled);

    var statePattern = 
        @"\b(?:" + 
        string.Join(
            "|",
            new [] {
                string.Join("|", states.Keys.Select(x => Regex.Escape(x))),
                string.Join("|", states.Values)
            }) +
        @")\b";

    var directionalPattern =
        string.Join(
            "|",
            new [] {
                string.Join("|", directionals.Keys),
                string.Join("|", directionals.Values),
                string.Join("|", directionals.Values.Select(x => Regex.Replace(x, @"(\w)", @"$1\.")))
            });

    var zipPattern = @"\d{5}(?:-?\d{4})?";

    var numberPattern =
        @"(
            ((?<NUMBER>\d+)(?<SECONDARYNUMBER>(-[0-9])|(\-?[A-Z]))(?=\b))    # Unit-attached
            |(?<NUMBER>\d+[\-\ ]?\d+\/\d+)                                   # Fractional
            |(?<NUMBER>\d+-?\d*)                                             # Normal Number
            |(?<NUMBER>[NSWE]\ ?\d+\ ?[NSWE]\ ?\d+)                          # Wisconsin/Illinois
          )";

    var streetPattern =
        string.Format(
            CultureInfo.InvariantCulture,
            @"
                (?:
                  # special case for addresses like 100 South Street
                  (?:(?<STREET>{0})\W+
                     (?<SUFFIX>{1})\b)
                  |
                  (?:(?<PREDIRECTIONAL>{0})\W+)?
                  (?:
                    (?<STREET>[^,]*\d)
                    (?:[^\w,]*(?<POSTDIRECTIONAL>{0})\b)
                   |
                    (?<STREET>[^,]+)
                    (?:[^\w,]+(?<SUFFIX>{1})\b)
                    (?:[^\w,]+(?<POSTDIRECTIONAL>{0})\b)?
                   |
                    (?<STREET>[^,]+?)
                    (?:[^\w,]+(?<SUFFIX>{1})\b)?
                    (?:[^\w,]+(?<POSTDIRECTIONAL>{0})\b)?
                  )
                )
            ",
            directionalPattern,
            suffixPattern);

    var rangedSecondaryUnitPattern =
        @"(?<SECONDARYUNIT>" +
        string.Join("|", rangedSecondaryUnits.Keys) +
        @")(?![a-z])";
    var rangelessSecondaryUnitPattern =
        @"(?<SECONDARYUNIT>" +
        string.Join(
            "|",
            string.Join("|", rangelessSecondaryUnits.Keys)) +
        @")\b";
    var allSecondaryUnitPattern = string.Format(
        CultureInfo.InvariantCulture,
        @"
            (
                (:?
                    (?: (?:{0} \W*)
                        | (?<SECONDARYUNIT>\#)\W*
                    )
                    (?<SECONDARYNUMBER>[\w-]+)
                )
                |{1}
            ),?
        ",
         rangedSecondaryUnitPattern,
         rangelessSecondaryUnitPattern);

    var cityAndStatePattern = string.Format(
        CultureInfo.InvariantCulture,
        @"
            (?:
                (?<CITY>[^\d,]+?)\W+
                (?<STATE>{0})
            )
        ",
        statePattern);
    var placePattern = string.Format(
        CultureInfo.InvariantCulture,
        @"
            (?:{0}\W*)?
            (?:(?<ZIP>{1}))?
        ",
        cityAndStatePattern,
        zipPattern);

    var addressPattern = string.Format(
        CultureInfo.InvariantCulture,
        @"
            ^
            # Special case for APO/FPO/DPO addresses
            (
                [^\w\#]*
                (?<STREETLINE>.+?)
                (?<CITY>[AFD]PO)\W+
                (?<STATE>A[AEP])\W+
                (?<ZIP>{4})
                \W*
            )
            |
            # Special case for PO boxes
            (
                \W*
                (?<STREETLINE>(P[\.\ ]?O[\.\ ]?\ )?BOX\ [0-9]+)\W+
                {3}
                \W*
            )
            |
            (
                [^\w\#]*    # skip non-word chars except # (eg unit)
                (  {0} )\W*
                   {1}\W+
                (?:{2}\W+)?
                   {3}
                \W*         # require on non-word chars at end
            )
            $           # right up to end of string
        ",
        numberPattern,
        streetPattern,
        allSecondaryUnitPattern,
        placePattern,
        zipPattern);
    addressRegex = new Regex(
        addressPattern,
        RegexOptions.Compiled | 
        RegexOptions.Singleline | 
        RegexOptions.IgnorePatternWhitespace);
}

【问题讨论】:

  • 带有回溯的正则表达式可能非常慢,尤其是在大输入时。尝试一次将您给表达式的输入限制为三行:在ParseAddress 方法中,将文本拆分为单独的行,并在循环中将其提供给正则表达式,行 {0,1,2},然后 { 1,2,3},然后是 {2,3,4},依此类推。找到匹配项时返回。
  • 您在移动代码时是否不小心修改了正则表达式?还是在复制粘贴时修改了输入?你初始化解析器了吗?还是您不小心双击了按钮,从而触发了库中的一些奇怪的错误?高内存消耗指向错误的正则表达式/输入组合 - 糟糕的运行时行为
  • 输入只是一个简单的美国地址,例如“1234 Main St, Anytown, NY 46521”。至于限制输入,考虑到 RegEx 的编写方式,我什至不知道如何做到这一点。基本上,他定义了一大组字典,然后将它们粘合在一起形成一个庞大的正则表达式来解析地址。最后,在移动代码时,我使用 VS 中的“添加现有项目”来导入 Parse 文件,正如您在上面看到的,我唯一更改的是方法的名称,这样更容易识别。至于输入不好,其实我都试过同一个地址。
  • 我在上面添加了RegEx 代码的示例,希望这可能会有所帮助。再一次,令人困惑的部分是它在简单的程序中工作,而不是在更大的程序中。而且我知道事实上这不是命名冲突,而且大型程序中也没有任何密集的内容。
  • @KevenM:当省略 RegexOptions.Compiled 标志时,Regex 是否有效?

标签: c# wpf regex parsing


【解决方案1】:

省略RegexOptions.Compiled 标志时,Regex 是否有效?

回答是肯定的。

那为什么?

Regex 编译器在(一些?)大模式下似乎很慢。

这是您必须做出的权衡。

【讨论】:

  • 只是想再次表示感谢,我现在有这个工作,它很完美!对于阅读此问题的其他任何人,请给 leppie 一个 upvote!
【解决方案2】:

一些正则表达式子表达式是不恰当的(正如@Justin Morgan 提到的那样)。
这通常是加入可重用的碎片正则表达式的结果,它使得
我畏缩。

但是,如果您要使用/执行这种方法,打印出
构造后的实际正则表达式。并且,在格式化之后,针对它进行测试
示例并独立于您的主程序进行操作。这样更容易修复。
在您看到可疑子表达式的地方,尝试让它在那个时候失败,或者
一般来说,尝试在样本末尾附近插入失败。如果需要超过
一眨眼就失败了,然后它的严重倒退。

不过,回溯也不错。它有很大的好处。没有它,有些东西
就是不匹配。诀窍是隔离不影响的子表达式
结果相对于它周围的东西,然后限制它的回溯。

我去了 USPS 网站并获取了一些示例状态/后缀/方向/次要
样本,足以生成地址正则表达式。以下是
的清理版本 从您的代码生成的正则表达式。

祝你好运!

 ^
   # Special case for APO/FPO/DPO addresses
   (
      [^\w\#]*
      (?<STREETLINE> .+? )
      (?<CITY> [AFD] PO )
      \W+
      (?<STATE> A [AEP] )
      \W+
      (?<ZIP> \d{5} (?: -? \d{4} )? )
      \W*
   )
 |         
   # Special case for PO boxes
   (
      \W*
      (?<STREETLINE> ( P [\.\ ]? O [\.\ ]? \  )? BOX \  [0-9]+ )
      \W+
      (?:
          (?:
              (?<CITY> [^\d,]+? )
              \W+
              (?<STATE>
                 \b
                 (?:AL|AK|AS|AZ|AR|Alabama|Alaska|American Samoa|Arizona|Arkansas)
                 \b
              )
          )
          \W*
      )?
      (?:
          (?<ZIP> \d{5} (?: -? \d{4} )? )
      )?
      \W*
   )
 |          
   (
       [^\w\#]*    # skip non-word chars except # (eg unit)
       (
         (
              (
                (?<NUMBER> \d+ )
                (?<SECONDARYNUMBER> (-[0-9]) | (\-?[A-Z]) )
                (?=\b)
              )                                                  # Unit-attached
           |          
             (?<NUMBER> \d+ [\-\ ]? \d+ \/ \d+ )                 # Fractional
           |
             (?<NUMBER> \d+ -? \d* )                             # Normal Number
           |
             (?<NUMBER>[NSWE]\ ?\d+\ ?[NSWE]\ ?\d+)              # Wisconsin/Illinois
         )
       )
       \W*

       (?:
           # special case for addresses like 100 South Street
           (?:
               (?<STREET>North|East|South|West|Northeast|Southeast|Northwest|Southwest|N|E|S|W|NE|SE|NW|SW|N\.|E\.|S\.|W\.|N\.E\.|S\.E\.|N\.W\.|S\.W\.)
               \W+
               (?<SUFFIX>ALLEY|ALY|ALLY|ALLEE|ALLEY|ALY)
               \b
           )
         |
           (?:
               (?<PREDIRECTIONAL>North|East|South|West|Northeast|Southeast|Northwest|Southwest|N|E|S|W|NE|SE|NW|SW|N\.|E\.|S\.|W\.|N\.E\.|S\.E\.|N\.W\.|S\.W\.)
               \W+
           )?
           (?:
                (?<STREET> [^,]* \d )
                (?:
                   [^\w,]*
                   (?<POSTDIRECTIONAL>North|East|South|West|Northeast|Southeast|Northwest|Southwest|N|E|S|W|NE|SE|NW|SW|N\.|E\.|S\.|W\.|N\.E\.|S\.E\.|N\.W\.|S\.W\.)
                   \b
                )
             |
                (?<STREET> [^,]+ )
                (?:
                    [^\w,]+
                    (?<SUFFIX>ALLEY|ALY|ALLY|ALLEE|ALLEY|ALY)
                    \b
                )
                (?:
                    [^\w,]+
                    (?<POSTDIRECTIONAL>North|East|South|West|Northeast|Southeast|Northwest|Southwest|N|E|S|W|NE|SE|NW|SW|N\.|E\.|S\.|W\.|N\.E\.|S\.E\.|N\.W\.|S\.W\.)
                    \b
                )?
             |
                (?<STREET> [^,]+? )
                (?:
                    [^\w,]+
                    (?<SUFFIX>ALLEY|ALY|ALLY|ALLEE|ALLEY|ALY)
                    \b
                )?
                (?:
                    [^\w,]+
                    (?<POSTDIRECTIONAL>North|East|South|West|Northeast|Southeast|Northwest|Southwest|N|E|S|W|NE|SE|NW|SW|N\.|E\.|S\.|W\.|N\.E\.|S\.E\.|N\.W\.|S\.W\.)
                    \b
                )?
           )
       )           

       \W+        

       (?:      
           (
               (
                  :?
                  (?:
                      (?:
                         (?<SECONDARYUNIT>APT|BLDG|DEPT|FL|HNGR|LOT|PIER|RM|SLIP|SPC|STOP|STE|TRLR|UNIT)
                         (?! [a-z] )
                         \W*
                       )
                    |
                       (?<SECONDARYUNIT> \# )
                       \W*
                  )
                  (?<SECONDARYNUMBER> [\w-]+ )
               )
             |
               (?<SECONDARYUNIT>BSMT|FRNT|LBBY|LOWR|OFC|PH|REAR|SIDE|UPPR)
               \b
           )
           ,?
           \W+
       )?

       (?:
           (?:
               (?<CITY> [^\d,]+? )
               \W+
               (?<STATE>
                  \b
                  (?:AL|AK|AS|AZ|AR|Alabama|Alaska|American Samoa|Arizona|Arkansas)
                  \b
               )
           )
           \W*
       )?

       (?:
           (?<ZIP> \d{5} (?: -? \d{4} )? )
       )?

       \W*         # require on non-word chars at end
   )
 $           # right up to end of string

C#代码

   public static void InitializeRegex()
    {
        Dictionary<string, string> suffixes = new Dictionary<string, string>()
        {
          {"ALLEY",  "ALLEE"},
          {"ALY",  "ALLEY"},
          {"ALLY",  "ALY"},
        };

        var suffixPattern = new Regex(
            string.Join(
                "|",
                new[] {
            string.Join("|", suffixes.Keys.ToArray()), 
            string.Join("|", suffixes.Values.Distinct().ToArray())
        }),
            RegexOptions.Compiled);

        //Console.WriteLine("\n"+suffixPattern);

        Dictionary<string, string> states = new Dictionary<string, string>()
        {
           {"AL", "Alabama"},
           {"AK", "Alaska"},
           {"AS",  "American Samoa"},
           {"AZ",  "Arizona"},
           {"AR", "Arkansas"}
        };

        var statePattern =
            @"\b(?:" +
            string.Join(
                "|",
                new[] {
            string.Join("|", states.Keys.Select(x => Regex.Escape(x)).ToArray()),
            string.Join("|", states.Values.ToArray())
        }) +
            @")\b";

        //Console.WriteLine("\n" + statePattern);

        Dictionary<string, string> directionals = new Dictionary<string, string>()
        {
           {"North", "N" },
           {"East", "E" },
           {"South", "S" },
           {"West", "W" },
           {"Northeast", "NE" },
           {"Southeast", "SE" },
           {"Northwest", "NW" },
           {"Southwest", "SW" }
        };

        var directionalPattern =
            string.Join(
                "|",
                new[] {
            string.Join("|", directionals.Keys.ToArray()),
            string.Join("|", directionals.Values.ToArray()),
            string.Join("|", directionals.Values.Select(x => Regex.Replace(x, @"(\w)", @"$1\.")).ToArray())
        });

        //Console.WriteLine("\n" + directionalPattern);

        var zipPattern = @"\d{5}(?:-?\d{4})?";

        //Console.WriteLine("\n" + zipPattern);

        var numberPattern =
            @"(
                ((?<NUMBER>\d+)(?<SECONDARYNUMBER>(-[0-9])|(\-?[A-Z]))(?=\b))    # Unit-attached
                |(?<NUMBER>\d+[\-\ ]?\d+\/\d+)                                   # Fractional
                |(?<NUMBER>\d+-?\d*)                                             # Normal Number
                |(?<NUMBER>[NSWE]\ ?\d+\ ?[NSWE]\ ?\d+)                          # Wisconsin/Illinois
             )";

        //Console.WriteLine("\n" + numberPattern);

        var streetPattern =
            string.Format(
                CultureInfo.InvariantCulture,
                @"
                    (?:
                      # special case for addresses like 100 South Street
                      (?:(?<STREET>{0})\W+
                         (?<SUFFIX>{1})\b)
                      |
                      (?:(?<PREDIRECTIONAL>{0})\W+)?
                      (?:
                        (?<STREET>[^,]*\d)
                        (?:[^\w,]*(?<POSTDIRECTIONAL>{0})\b)
                       |
                        (?<STREET>[^,]+)
                        (?:[^\w,]+(?<SUFFIX>{1})\b)
                        (?:[^\w,]+(?<POSTDIRECTIONAL>{0})\b)?
                       |
                        (?<STREET>[^,]+?)
                        (?:[^\w,]+(?<SUFFIX>{1})\b)?
                        (?:[^\w,]+(?<POSTDIRECTIONAL>{0})\b)?
                      )
                    )
                ",
                directionalPattern,
                suffixPattern);

        //Console.WriteLine("\n" + streetPattern);


        Dictionary<string, string> rangedSecondaryUnits = new Dictionary<string, string>()
        {
            {"APT",  "APARTMENT"},
            {"BLDG", "BUILDING"}, 
            {"DEPT", "DEPARTMENT"}, 
            {"FL",   "FLOOR"}, 
            {"HNGR", "HANGAR"}, 
            {"LOT",  "LOT"}, 
            {"PIER", "PIER"}, 
            {"RM",   "ROOM"}, 
            {"SLIP", "SLIP"}, 
            {"SPC",  "SPACE"}, 
            {"STOP", "STOP"}, 
            {"STE",  "SUITE"}, 
            {"TRLR", "TRAILER"}, 
            {"UNIT", "UNIT"} 
        };
        var rangedSecondaryUnitPattern =
            @"(?<SECONDARYUNIT>" +
            string.Join("|", rangedSecondaryUnits.Keys.ToArray()) +
            @")(?![a-z])";

        //Console.WriteLine("\n" + rangedSecondaryUnitPattern);


        Dictionary<string, string> rangelessSecondaryUnits = new Dictionary<string, string>()
        {
            {"BSMT", "BASEMENT"},
            {"FRNT", "FRONT"},
            {"LBBY", "LOBBY"},
            {"LOWR", "LOWER"},
            {"OFC",  "OFFICE"},
            {"PH",   "PENTHOUSE"},
            {"REAR", "REAR"},
            {"SIDE", "SIDE"},
            {"UPPR", "UPPER"}
        };

        var rangelessSecondaryUnitPattern =
            @"(?<SECONDARYUNIT>" +
            string.Join("|", rangelessSecondaryUnits.Keys.ToArray()) +
            @")\b";

        //Console.WriteLine("\n" + rangelessSecondaryUnitPattern);

        var allSecondaryUnitPattern = string.Format(
            CultureInfo.InvariantCulture,
            @"
                (
                    (:?
                        (?: (?:{0} \W*)
                            | (?<SECONDARYUNIT>\#)\W*
                        )
                        (?<SECONDARYNUMBER>[\w-]+)
                    )
                    |{1}
                ),?
            ",
             rangedSecondaryUnitPattern,
             rangelessSecondaryUnitPattern);

        //Console.WriteLine("\n" + allSecondaryUnitPattern);

        var cityAndStatePattern = string.Format(
            CultureInfo.InvariantCulture,
            @"
                (?:
                    (?<CITY>[^\d,]+?)\W+
                    (?<STATE>{0})
                )
            ",
            statePattern);

        //Console.WriteLine("\n" + cityAndStatePattern);

        var placePattern = string.Format(
            CultureInfo.InvariantCulture,
            @"
                (?:{0}\W*)?
                (?:(?<ZIP>{1}))?
            ",
            cityAndStatePattern,
            zipPattern);

        //Console.WriteLine("\n" + placePattern);

        var addressPattern = string.Format(
            CultureInfo.InvariantCulture,
            @"
                ^
                # Special case for APO/FPO/DPO addresses
                (
                    [^\w\#]*
                    (?<STREETLINE>.+?)
                    (?<CITY>[AFD]PO)\W+
                    (?<STATE>A[AEP])\W+
                    (?<ZIP>{4})
                    \W*
                )
                |
                # Special case for PO boxes
                (
                    \W*
                    (?<STREETLINE>(P[\.\ ]?O[\.\ ]?\ )?BOX\ [0-9]+)\W+
                    {3}
                    \W*
                )
                |
                (
                    [^\w\#]*    # skip non-word chars except # (eg unit)
                    (  {0} )\W*
                       {1}\W+
                    (?:{2}\W+)?
                       {3}
                    \W*         # require on non-word chars at end
                )
                $           # right up to end of string
            ",
            numberPattern,
            streetPattern,
            allSecondaryUnitPattern,
            placePattern,
            zipPattern);

        Console.WriteLine("\n-----------------------------\n\n" + addressPattern);

        var addressRegex = new Regex(
            addressPattern,
            RegexOptions.Compiled |
            RegexOptions.Singleline |
            RegexOptions.IgnorePatternWhitespace);

    }

【讨论】:

    【解决方案3】:

    像这样逐渐增加资源使用是catastrophic backtracking 的确凿证据。基本上,如果你有类似的东西,比如说,这部分:

    (?<CITY>[^\d,]+?)\W+
    

    ...那么输入的哪一部分与模式的哪一部分匹配就会有歧义。几乎任何匹配\W 的东西也可以匹配[^\d,]。如果输入在第一次传递时未能匹配,引擎将返回并尝试这两组的不同排列,这会消耗资源。

    例如,假设您输入的“城市”部分后面有一大堆空格。一长串空格将匹配[^\d,]+?\W+,因此尚不清楚 CITY 组是否包含空格。基于这些量词的惰性/贪婪行为,引擎将尝试仅将城市名称放入[^\d,]+?,并将所有空格放入\W+。然后它会继续前进并尝试匹配输入的其余部分。

    如果输入的其余部分在第一次尝试时匹配,那很好。但是,如果匹配失败,则必须返回并重试,这次其中一个空间与 [^\d,]+? 匹配并被捕获为 CITY 组的一部分。如果失败,它将使用两个空格重试,依此类推。

    您通常会看到这成为嵌套量词的问题,例如类似([ABC]+)*。我在您的模式中看不到任何正在发生的事情,但我可能在所有string.Format 电话中错过了一些东西。我的猜测是它是一个很长的模式,有很多量词和交流器要回溯(还有很多组要存储),即使是单级迭代也会让你丧命。我敢打赌,长输入字符串匹配 大多数 模式,但无法匹配所有模式,对性能的影响最大。

    在这种情况下编译正则表达式可能会有所帮助,您应该这样做。但是,当您的应用程序一次获得一千次(或多少次)点击时,我怀疑这会减少它。还会有某些输入字符串会导致大量回溯,并在性能方面对您造成更大的打击。我最大的建议是找到并解决模式中的歧义。

    找到有很多量词的地方,例如 *+ 彼此靠近,并确保它们之间有明确的、非可选的分隔符(例如,您的 NUMBER 组中的 \d+-?\d* 会表现得更好,因为\d+(-\d*)?,或者更好的是\d+(-\d+)?\b)。最后,确保分隔符不能匹配它们旁边的标记。举一个虚构的例子,如果你给它输入一长串空格,\W+\ \W+ 之类的东西会一直拖下去。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-21
      • 2016-04-28
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      • 2011-10-21
      • 1970-01-01
      相关资源
      最近更新 更多