2

我需要创建一个允许以下所有三种可能性的单个正则表达式(使用 .NET 3.5 正则表达式,如果这很重要):

  1. [A-Z]{2,3}
  2. [A-Z]{2,3}-[A-Z][A-Z0-9]{1,2}
  3. [A-Z]{2,3}-[A-Z][A-Z0-9]{1,2}-[A-Z0-9]{2,3}

所以BIG, BIG-A9, orBIG-A09-SD会匹配,但是BIG-F, BIG-A9S-, orBIG-A09-S不会匹配。

我应该澄清一些事情:

  • 我需要匹配整条线,而不仅仅是其中的一部分。

  • 我正在尝试为正在将此字符串输入到TextBox. 因此,我正在寻找一个正则表达式,我可以用它来测试 TextBox 的内容,只要它发生变化。例如,当用户输入“A”时,反馈应该是否定的,因为他们还没有匹配案例 1。一旦他们输入“AA”,反馈就会是肯定的,因为他们匹配了案例 1。输入“AAE”后,他们应该仍然收到正面反馈,因为他们匹配案例 1。如果他们输入“AA7”,反馈应该是负面的。如果他们然后继续输入并输入“AAE-”,则反馈应该再次变为否定,因为他们还没有遇到案例 2,并且已经超过了案例 1。

我正在拔头发(剩下的很少)。

有人知道如何以一种可行的方式表达这一点吗?

4

3 回答 3

5

您的正则表达式模式可以根据作为其他前缀的属性进行排序。所以尝试嵌套可选后缀:

(?:[A-Z]{2,3}(?:-[A-Z][A-Z0-9]{1,2}(?:-[A-Z0-9]{2,3})?)?)
于 2013-08-16T16:55:46.960 回答
3

通常,正则表达式根据最左边最长的规则匹配,其形式表达式(来自 Posix 标准)是:

搜索的执行就像字符串的所有可能的后缀都已针对匹配模式的前缀进行了测试;选择包含匹配前缀的最长后缀,并将所选后缀的最长可能匹配前缀标识为匹配序列。

您的三种可能性中的每一种都是前一种可能性的扩展。从你的第三个也是最长的开始:

[A-Z]{2,3}-[A-Z][A-Z0-9]{1,2}-[A-Z0-9]{2,3}

它有 3 个组件,每个组件本身就是一个正则表达式:

  • [A-Z]{2,3}
  • -[A-Z][A-Z0-9]{1,2}
  • -[A-Z0-9]{2,3}

首先,您需要对它们中的每一个进行分组:

  • ([A-Z]{2,3})
  • (-[A-Z][A-Z0-9]{1,2})
  • (-[A-Z0-9]{2,3})

然后,最后两个可以组合成一个更长的正则表达式

  • ([A-Z]{2,3})
  • (-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})

请注意,第三段是可选的,如果我们有第二段,那么使用后缀运算符“?” 做到这一点:

  • ([A-Z]{2,3})
  • (-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})?

现在将整个第二个组件分组以表明它是一个整体:

  • ([A-Z]{2,3})
  • ((-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})?)

请注意,如果我们有第一个组件,那么整个第二个组件都是可选的,因此再次使用后缀运算符?来指示可选性:

  • ([A-Z]{2,3})
  • ((-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})?)?

最后,连接两个正则表达式以形成最终表达式:

([A-Z]{2,3})((-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})?)?

你去吧。正则表达式引擎不需要回溯和最小的前瞻。

编辑注释从下面的 OP 评论来看,所需的匹配似乎是整个 string。所以...

默认情况下,正则表达式在源字符串中查找最左边的最长匹配项。如果要匹配整个字符串整行,则需要使用元模式锚定表达式,^,它将该位置的匹配锚定到行首和$,将匹配锚定在该位置的行尾. 所以...

  • ^abcabc, 匹配前缀,匹配任何以( abc, abcabc, abcc, aabcdefg, 等)开头的字符串。xyzabc它与,ab等内容不匹配。

  • abc$类似地,匹配一个后缀,匹配任何以abc( abc,xyzabc等) 结尾的行。ab它与, xyzab,abcxyz等内容不匹配。

  • ^abc$只会匹配字符串而不abc匹配其他内容。

要匹配整行,请使用上述正则表达式并放置锚点:

^([A-Z]{2,3})((-[A-Z][A-Z0-9]{1,2})(-[A-Z0-9]{2,3})?)?$

这里的所有都是它的。然而,应该注意的是.^和的行为$取决于 或 正则表达式是否使用MultilineorSingleLine选项编译。SingleLine表示^and$仅匹配整个字符串的开头和结尾,并且.匹配除 . 之外的任何字符\nMultiLine表示^and$匹配字符串中任何行的开头和结尾,并.匹配任何字符,包括 \n.

如果要找出匹配的各种组件,可以在正则表达式中添加一些标记来创建命名组

(?<prefix>[A-Z]{2,3})((-(?<middle>[A-Z][A-Z0-9]{1,2}))(-(?<suffix>[A-Z0-9]{2,3}))?)?

这使您可以按名称访问匹配的组,从而使代码更具可读性:

Regex rx = new Regex( @"(?<prefix>[A-Z]{2,3})((-(?<middle>[A-Z][A-Z0-9]{1,2}))(-(?<suffix>[A-Z0-9]{2,3}))?)?" , RegexOptions.ExplicitCapture );
Match m = rx.Match(s) ;

if ( m.Success )
{
  string prefix = m.Groups["prefix"].Value ;
  string middle = m.Groups["middle"].Value ;
  string suffix = m.Groups["suffix"].Value ;

  Console.WriteLine( "prefix: {0}" , prefix ) ;
  Console.WriteLine( "middle: {0}" , middle ) ;
  Console.WriteLine( "suffix: {0}", suffix  ) ;

}

性能提示:使用命名组和编译正则表达式RegexOptions.ExplicitCapture会有很大帮助,尤其是当你的正则表达式变得复杂时。

如果您不熟悉正则表达式(即使您不是!),您应该阅读Jeffrey Friedl最优秀的作品掌握正则表达式

掌握正则表达式

于 2013-08-16T18:16:21.313 回答
0

就我而言,尼古拉斯凯里提供了答案。请参阅他的大部分答案。书已经订好了!

为了满足我在用户键入输入时向用户提供持续反馈的要求,我将以下内容注册为 Windows 窗体的 TextChanged 事件处理程序。

    private void TheTextChanged(object sender, EventArgs e)
    {
        var rx = new Regex(@"(?<prefix>[A-Z]{2,3})((-(?<middle>[A-Z][A-Z0-9]{1,2}))(-(?<suffix>[A-Z0-9]{2,3}))?)?", RegexOptions.ExplicitCapture);
        Match m = rx.Match(textbox.Text);

        if (m.Success)
        {
            string prefix = m.Groups["prefix"].Value;
            string middle = m.Groups["middle"].Value;
            string suffix = m.Groups["suffix"].Value;
            if (prefix != "")
            {
                textbox.BackColor = prefix.Equals(textbox.Text) ? Color.LightGreen : Color.LightPink;
                if (middle != "")
                {
                    string twoparts = prefix + "-" + middle;
                    textbox.BackColor = twoparts.Equals(textbox.Text) ? Color.LightGreen : Color.LightPink;
                    if (suffix != "")
                    {
                        string threeparts = prefix + "-" + middle + "-" + suffix;
                        textbox.BackColor = threeparts.Equals(textbox.Text) ? Color.LightGreen : Color.LightPink;
                    }
                }
            }
        }
        else
        {
            textbox.BackColor = Color.LightPink;
        }
    }

这是一个 WPF 版本,只是因为。

    private void TheTextChanged(object sender, TextChangedEventArgs e)
    {
        var rx = new Regex(@"(?<prefix>[A-Z]{2,3})((-(?<middle>[A-Z][A-Z0-9]{1,2}))(-(?<suffix>[A-Z0-9]{2,3}))?)?", RegexOptions.ExplicitCapture);
        Match m = rx.Match(textbox.Text);

        if (m.Success)
        {
            string prefix = m.Groups["prefix"].Value;
            string middle = m.Groups["middle"].Value;
            string suffix = m.Groups["suffix"].Value;
            if (prefix != "")
            {
                textbox.Background = prefix.Equals(textbox.Text) ? Brushes.LightGreen : Brushes.LightPink;
                if (middle != "")
                {
                    string twoparts = prefix + "-" + middle;
                    textbox.Background = twoparts.Equals(textbox.Text) ? Brushes.LightGreen : Brushes.LightPink;
                    if (suffix != "")
                    {
                        string threeparts = prefix + "-" + middle + "-" + suffix;
                        textbox.Background = threeparts.Equals(textbox.Text) ? Brushes.LightGreen : Brushes.LightPink;
                    }
                }
            }
        }
        else
        {
            textbox.Background = Brushes.LightPink;
        }
    }

感谢所有伟大的答案!下次我会尝试更明确。

于 2013-08-17T03:04:15.073 回答