如何使用正则表达式将具有复杂规则的固定宽度字符串解析为组件字段

时间:2012-10-20 17:31:36

标签: c# regex

我需要使用c#和Regular Expressions解析固定宽度的记录。 每条记录包含许多固定宽度字段,每个字段可能具有非平凡的验证规则。我遇到的问题是在固定宽度字段边界上应用匹配。

如果没有规则,很容易将长度为13的固定宽度的字符串拆分为4个部分,如下所示:

(?=^.{13}$).{1}.{5}.{6}.{1}

以下是示例字段规则: 字段可以是所有空格或以[A-Z]开头,右边用空格填充。字母之间不能出现空格

如果该字段是我必须验证的唯一内容,我可以使用它:

(?=^[A-Z ]{5}$)([ ]{5}|[A-Z]+[ ]*)

当我将此验证作为较长列表的一部分添加时,我必须从前瞻中删除^和$,然后我开始获得长度不是5的匹配。

这是完整的正则表达式以及一些应该与表达式匹配且不匹配的示例文本。

(?=^[A-Z ]{13}$)A(?=[A-Z ]{5})([ ]{5}|(?>[A-Z]{1,5})[ ]{0,4})(?=[A-Z ]{6})([ ]{6}|(?>[A-Z]{1,6})[ ]{0,5})Z

如何实施规则,以便对于每个字段,使用紧接的下一个XX字符进行匹配并确保匹配不重叠?

应匹配的行:

ABCDEFGHIJKLZ
A           Z
AB          Z
A     G     Z
AB    G     Z
ABCDEF      Z
ABCDEFG     Z
A     GHIJKLZ
AB    GHIJKLZ

不匹配的行:

AB D        Z
AB D F      Z
AB   F      Z
A     G I   Z
A     G I  LZ
A     G    LZ
AB   FG    LZ
AB D FG     Z
AB   FG I   Z
AB D FG i   Z

以下3个不匹配,但确实如此。

AB   FG     Z
AB   FGH    Z
AB  EFGH    Z

编辑:

一般解决方案(基于Ωmega的答案),为清晰起见,带有命名捕获:

(?<F1>F1Regex)(?<=^.{Len(F1)})
(?<F2>F2Regex)(?<=^.{Len(F1+F2)})
(?<F3>F3Regex)(?<=^.{Len(F1+F2+F3)})
               ...
(?<Fn>FnRegex)

另一个例子:正则表达式和零宽度正向回顾之间的空格(?&lt; =是为了清晰。

(?<F1>\d{2})    (?<=^.{2})
(?<F2>[A-Z]{5}) (?<=^.{7})
(?<F3>\d{4})    (?<=^.{11})
(?<F4>[A-Z]{6}) (?<=^.{17})
(?<F5>\d{4})

4 个答案:

答案 0 :(得分:1)

如果输入字符串的大小是固定的,那么您可以使用前瞻和后视来匹配特定位置,如下所示:

(?<=^.{s})(?<fieldName>.*)(?=.{e}$)

其中:

  • s =开始位置
  • e =字符串长度 - 匹配长度 - s

如果连接多个正则表达式,就像这个一样,那么你将获得具有特定定位的所有字段。

示例

  • 固定长度:10
  • 字段1:开始0,长度3
  • 字段2:开始3,长度5
  • 字段3:开始8,长度2

使用此正则表达式,忽略空格:

var match = Regex.Match("0123456789", @"
    (?<=^.{0})(?<name1>.*)(?=.{7}$)
    (?<=^.{3})(?<name2>.*)(?=.{2}$)
    (?<=^.{8})(?<name3>.*)(?=.{0}$)",
        RegexOptions.IgnorePatternWhitespace)

var field1 = match.Groups["name1"].Value;
var field2 = match.Groups["name2"].Value;
var field3 = match.Groups["name3"].Value;

您可以放置​​任何您想要匹配字段的规则。

我为所有人使用了.*,但你可以在那里放置任何东西。

示例2

var match = Regex.Match(" 1a any-8888", @"
    (?<=^.{0})(?<name1>\s*\d*[a-zA-Z])(?=.{9}$)
    (?<=^.{3})(?<name2>.*)(?=.{4}$)
    (?<=^.{8})(?<name3>(?<D>\d)\k<D>*)(?=.{0}$)
    ",
        RegexOptions.IgnorePatternWhitespace)

var field1 = match.Groups["name1"].Value; // " 1a"
var field2 = match.Groups["name2"].Value; // " any-"
var field3 = match.Groups["name3"].Value; // "8888"

这是你的正则表达式

我测试了所有这些,但是这个样本与你说的那个不应该通过,但是通过了...这次,它不会通过:

var match = Regex.Match("AB   FG     Z", @"
    ^A
    (?<=^.{1})  (?<name1>([ ]{5}|(?>[A-Z]{1,5})[ ]{0,4}))  (?=.{7}$)
    (?<=^.{6})  (?<name2>([ ]{6}|(?>[A-Z]{1,6})[ ]{0,5}))  (?=.{1}$)
    Z$
    ",
        RegexOptions.IgnorePatternWhitespace)

// no match with this input string

答案 1 :(得分:1)

Match match = Regex.Match(
  Regex.Replace(text, @"^(.)(.{5})(.{6})(.)$", "$1,$2,$3,$4"),
  @"^[A-Z ],[A-Z]*[ ]*,[A-Z]*[ ]*,[A-Z ]$");

检查此代码 here

答案 2 :(得分:0)

我认为可以通过单个正则表达式模式验证它

^[A-Z ][A-Z]*[ ]*(?<=^.{6})[A-Z]*[ ]*(?<=^.{12})[A-Z ]$

如果您还需要捕获所有此类组,请使用

^([A-Z ])([A-Z]*[ ]*)(?<=^.{6})([A-Z]*[ ]*)(?<=^.{12})([A-Z ])$

答案 3 :(得分:0)

我之前已经发布了这个,但这个答案更具体针对您的问题,而不是一般化的。

这解决了您在问题中提出的所有案例,即您想要的方式。

计划测试您问题中的所有案例

class Program
{
    static void Main()
    {
        var strMatch = new string[]
                      {
                          // Lines that should match:
                          "ABCDEFGHIJKLZ",
                          "A           Z",
                          "AB          Z",
                          "A     G     Z",
                          "AB    G     Z",
                          "ABCDEF      Z",
                          "ABCDEFG     Z",
                          "A     GHIJKLZ",
                          "AB    GHIJKLZ",
                      };


        var strNotMatch = new string[]
                      {
                          // Lines that should not match:
                          "AB D        Z",
                          "AB D F      Z",
                          "AB   F      Z",
                          "A     G I   Z",
                          "A     G I  LZ",
                          "A     G    LZ",
                          "AB   FG    LZ",
                          "AB D FG     Z",
                          "AB   FG I   Z",
                          "AB D FG i   Z",

                          // The following 3 should not match but do.
                          "AB   FG     Z",
                          "AB   FGH    Z",
                          "AB  EFGH    Z",
                      };

        var pattern = @"
                ^A
                (?<=^.{1})  (?<name1>([ ]{5}|(?>[A-Z]{1,5})[ ]{0,4}))  (?=.{7}$)
                (?<=^.{6})  (?<name2>([ ]{6}|(?>[A-Z]{1,6})[ ]{0,5}))  (?=.{1}$)
                Z$
                ";

        foreach (var eachStrThatMustMatch in strMatch)
        {
            var match = Regex.Match(eachStrThatMustMatch,
                pattern, RegexOptions.IgnorePatternWhitespace);

            if (!match.Success)
                throw new Exception("Should match.");
        }


        foreach (var eachStrThatMustNotMatch in strNotMatch)
        {
            var match = Regex.Match(eachStrThatMustNotMatch,
                pattern, RegexOptions.IgnorePatternWhitespace);

            if (match.Success)
                throw new Exception("Should match.");
        }
    }
}