我前几天试过问这个问题,诚然一开始没有很好地表述问题或发布代码,答案被关闭了。所以我在这里再次尝试,因为老实说,这很快就会让我发疯。 :)
我正在尝试实现这个 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();
}
解析地址方法
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);
}
最佳答案
当省略 RegexOptions.Compiled
标志时,Regex 是否工作?
回答是肯定的。
为什么?
似乎 Regex 编译器对于(某些?)大模式很慢。
这是您必须做出的权衡。
关于c# - 大型 RegEx 匹配导致程序挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10688115/