正则表达式模式匹配以模式开头的组

时间:2016-11-17 15:15:22

标签: regex c#-4.0

我从文本流中提取数据,这是一种数据结构

/1-<id>/<recType>-<data>..repeat n times../1-<id>/#-<data>..repeat n times.. 

在上面,&#34; / 1&#34;字段在记录数据之前,然后可以包含任意数量的以下字段,每个字段可以选择recType从2到9(同样,每个字段以&#34; /&#34开头)

例如:

 /1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE

所以,上面有三组数据

 1=XXXX 2=YYYY  9=ZZZZ
 1=AAAA 3=BBBB  5=CCCC  8=NNNN  9=DDDD
 1=QQQQ 2=WWWW  3=PPPP  7=EEEE

数据是为了简单起见,我确信它只包含[A-Z0-9。 ]但可以是可变长度(不仅仅是4个字符)

现在,以下表达式有效,但它只捕获每个组的前2个字段而没有剩余的字段......

/1-(?'fld1'[A-Z]+)/((?'fldNo'[2-9])-(?'fldData'[A-Z0-9\. ]+))

我知道我某处需要某种quantifier,但我不知道放在哪里或放在哪里。

3 个答案:

答案 0 :(得分:1)

您可以使用正则表达式来匹配这些块,使用2个.NET正则表达式功能:1)捕获集合和2)模式中具有相同名称的多个捕获组。然后,我们需要一些Linq魔法将捕获的数据合并到一个列表列表中:

(?<fldNo>1)-(?'fldData'[^/]+)(?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))*

<强>详情:

  • (?<fldNo>1) - 小组fldNo匹配1
  • - - 连字符
  • (?'fldData'[^/]+) - Group&#34; fldData&#34;捕获除/
  • 以外的1个字符
  • (?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))* - 零个或多个序列:
    • / - 文字/
    • (?<fldNo>[2-9]) - 29位数(Group&#34; fldNo&#34;)
    • [-=] - -=
    • (?'fldData'[^/]+) - 除/以外的1个字符(Group&#34; fldData&#34;)

请参阅regex demo,结果:

enter image description here

请参阅C# demo

using System;
using System.Linq;
using System.Text.RegularExpressions;
public class Test
{
    public static void Main()
    {
        var str = "/1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE";
        var res = Regex.Matches(str, @"(?<fldNo>1)-(?'fldData'[^/]+)(?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))*")
            .Cast<Match>()
            .Select(p => p.Groups["fldNo"].Captures.Cast<Capture>().Select(m => m.Value)
                    .Zip(p.Groups["fldData"].Captures.Cast<Capture>().Select(m => m.Value), 
                        (first, second) => first + "=" + second))
            .ToList(); 
            foreach (var t in res)
                Console.WriteLine(string.Join(" ", t));
    }
}

答案 1 :(得分:0)

我建议首先将字符串拆分为/1,然后沿着这些行使用patern:

\/([1-9])[=-]([A-Z]+)

https://regex101.com/r/0nyzzZ/1

答案 2 :(得分:0)

单个正则表达式不是执行此操作的最佳工具(至少以这种方式使用)。主要原因是因为您的流中包含可变数量的条目,并且不支持使用可变数量的捕获组。我还注意到一些值有&#34; =&#34;它们之间以及破折号,你当前的正则表达式都没有解决。

当您尝试将量词添加到捕获组时,问题就出现了 - 该组只会记住它捕获的最后一个东西,因此如果添加量词,它将最终捕获第一个和最后一个字段,而忽略所有字段其余的人。所以这样的事情不会起作用:

\/1-(?'fld1'[A-Z]+)(?:\/(?'fldNo'[2-9])[-=](?'fldData'[A-Z]+))+

如果您的流的长度都相同,那么可以使用单个正则表达式,但是有一种方法可以使用foreach循环来执行此操作,其中使用更简单的正则表达式处理流的每个部分(因此它会验证你的流也一样!)

现在我不确定您在使用这种语言时使用的是哪种语言,但我认为这是一种PHP解决方案,可以满足您的需求。

function extractFromStream($str) 
{
    /*
     * Get an array of [num]-[letters] with explode. This will make an array that
     * contains [0] => 1-AAAA, [1] => 2-BBBB ... etc
     */

    $arr    = explode("/", substr($str, 1));

    $sorted = array();
    $key    = 0;

    /*
     * Sort this data into key->values based on numeric ordering.
     * If the next one has a lower or equal starting number than the one before it, 
     * a new entry will be created. i.e. 2-aaaa => 1-cccc will cause a new
     * entry to be made, just in case the stream doesn't always start with 1.
     */

    foreach ($arr as $value) 
    {   
        // This will get the number at the start, and has the added bonus of making sure
        // each bit is in the right format.
        if (preg_match("/^([0-9]+)[=-]([A-Z]+)$/", $value, $matches)) {
            $newKey = (int)$matches[1];
            $match  = $matches[2];
        } else
            throw new Exception("This is not a valid data stream!");

        // This bit checks if we've got a lower starting number than last time.
        if (isset($lastKey) && is_int($lastKey) && $newKey <= $lastKey)
            $key += 1;

        // Now sort them..
        $sorted[$key][$newKey] = $match;

        // This will be compared in the next iteration of the loop.
        $lastKey = $newKey;
    }

    return $sorted;
}

以下是如何使用它...

$full = "/1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE";

try {
    $extracted = extractFromStream($full);
    $stream1   = $extracted[0];
    $stream2   = $extracted[1];
    $stream3   = $extracted[2];

    print "<pre>";
    echo "Full extraction: \n";
    print_r($extracted);
    echo "\nFirst Stream:\n";
    print_r($stream1);
    echo "\nSecond Stream:\n";
    print_r($stream2);
    echo "\nThird Stream:\n";
    print_r($stream3);
    print "</pre>";
} catch (Exception $e) {
    echo $e->getMessage();
}

这将打印

Full extraction: 
Array
(
    [0] => Array
        (
            [1] => XXXX
            [2] => YYYY
            [9] => ZZZZ
        )

    [1] => Array
        (
            [1] => AAAA
            [3] => BBBB
            [5] => CCCC
            [8] => NNNN
            [9] => DDDD
        )

    [2] => Array
        (
            [1] => QQQQ
            [2] => WWWW
            [3] => PPPP
            [7] => EEEE
        )

)

First Stream:
Array
(
    [1] => XXXX
    [2] => YYYY
    [9] => ZZZZ
)

Second Stream:
Array
(
    [1] => AAAA
    [3] => BBBB
    [5] => CCCC
    [8] => NNNN
    [9] => DDDD
)

Third Stream:
Array
(
    [1] => QQQQ
    [2] => WWWW
    [3] => PPPP
    [7] => EEEE
)

因此,您可以看到数字作为数组键,以及它们对应的值,现在可以轻松访问以进行进一步处理。我希望这可以帮助你:)