如何将带有复杂规则的宽度字符串解析为带有正则式的组件字段



我需要使用C#和正则表达式来解析固定宽度记录。每个记录都包含许多固定宽度字段,每个字段可能具有非平凡的验证规则。我遇到的问题是在固定宽度范围边界上应用匹配项。

没有规则,很容易将长度13的固定宽度字符串分解为4个部分:

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

这是一个示例字段规则:字段可以是所有空间,也可以从[A-Z]开始,然后用空格填充。在字母之间不可能发生空间

如果该字段是我唯一要验证的东西,我可以使用它:

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

当我添加此验证作为更长列表的一部分时,我必须从lookahead中删除 ^和$,然后开始获得不长的匹配项。

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

(?=^[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})

如果输入字符串已固定为大小,则可以使用look-aheads和look-behinds匹配特定位置,例如:

(?<=^.{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
Match match = Regex.Match(
  Regex.Replace(text, @"^(.)(.{5})(.{6})(.)$", "$1,$2,$3,$4"),
  @"^[A-Z ],[A-Z]*[ ]*,[A-Z]*[ ]*,[A-Z ]$");

检查此代码在此处

我认为有可能通过单条正则态度进行验证

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

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

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

我以前已经发布了此内容,但是这个答案更适合您的问题,而不是概括。

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

程序以测试您的问题中的所有情况

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.");
        }
    }
}

最新更新