正则表达式管道字符用作子组内的分隔符

时间:2018-05-19 17:24:04

标签: c# regex pipe delimiter

我有一个由管道字符分隔的字符串。这是一个可重复的序列:

AutoDeltaSettings

但是,如果您看到items标记,则还会通过其间的管道字符分隔itemnumbers。好吧,它的'不是一个智能格式,但我必须解决它,我想在C#中使用正则表达式。所以假设上面的格式让我们有一个真实的例子:

<machinenr>|<controldone>|<nrofitems|<items>

理论上注意,管道之间不需要是一个值,内容也不受长度的限制。项目ID可以是111或877333,但即使是混合的字母数字ID XB111。所以我们这里有两台没有物品的机器:

446408|0|2|111|6847|446408||0||

这里有一些没有或有些物品的机器。注意,管道字符也用于分隔项目,因此管道内有管道:

446408|0|0||447400||0||

这台机器有三个项目: 446408 | 0 | 3 | 99884 | 111 | 73732 |

项目ID:

446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0||

正则表达式应该是什么样的?我已尝试使用以下命名的群组(更易于阅读),但它不起作用:

99884|111|73732

以下是对@Atterson @sln和@的澄清。注意,项目数量可以是0-n,金额没有限制。让我们举个例子,一个带机器的长字符串及其项目:446408 | 0 | 1 | 111 | 446408 | 0 | 3 | 99884 | 111 | 73732 | 446408 | 0 | 0 ||我期望正则表达式做的是将这个字符串分成三个匹配/部分及其值,第一个匹配是:446408 | 0 | 1 | 111 |第二场比赛:446408 | 0 | 3 | 99884 | 111 | 73732 |和第三场比赛:446408 | 0 | 0 ||好的,为了举例说明每个部分应分成的值,让我们使用第二个匹配/部分。它是一台nr 446408的机器,它没有被控制0,它有3个项目,项目ID:99884 | 111 | 73732。在这些项目之后,出现了一个新序列:

^(?P<machinenr>.*?)\|
(?P<controldone>.*?)\|
(?P<nrofitems>.*?)\|
(?P<items>.*?)\|

可以跟随。 @Sanxofon请在这里检查你的正则表达式:[link] https://regex101.com/r/kC3gH0/87并且你很遗憾地看到它不匹配。

2 个答案:

答案 0 :(得分:1)

这不能用正则表达式解决,没有办法告诉正则表达式如下:“匹配.*?\|与特定捕获组相同的次数......恰好包含一个数字。 “这是使用普通旧C#的直接解决方案。

string items = "446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0|";
var fields = items.Split('|');
for (int i = 0; i < fields.Length;) {
    Console.WriteLine("machinenr:" + fields[i++]);
    Console.WriteLine("controldone:" + fields[i++]);
    int numSubItems = Int32.Parse(fields[i++]);
    Console.WriteLine("num subitems:" + numSubItems);
    if (numSubItems == 0) {
        i++;
        continue;
    }                

    for (int subItemIndex = 0; subItemIndex < numSubItems; subItemIndex++) {
        Console.WriteLine("\tItem:" + (subItemIndex + 1) + ": " + fields[i++]);
    }                
}

仅供参考,我修剪了尾随的“|”原来的字符串有,所以

string items = "446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0|";

而不是

string items = "446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0||";

答案 1 :(得分:0)

C#中的命名捕获组为(?<nam>...)而非(?P<name>...)。此外,你表达了重复匹配的愿望(所以我将你的正则表达式包裹在重复的(?<grp>..)中。

您需要弄清楚如何区分项目与计算机。例如,如果您可以说所有机器编号都是6位数,而项目是0-5位数,您可以执行类似的操作......您仍然需要拆分items集合。

^(?<grp>(?<machinenr>[^\|]{6})\|
(?<controldone>[^\|]*)\|
(?<nrofitems>[^\|]*)\|
(?<items>(?:[^\|]{0,5}\|){1,}))*$

示例C#实现:

class Program
{

    static void Main(string[] args)
    {
        string strRegex = 
@"^(?<grp>(?<machinenr>[^\|]{6})\|
(?<controldone>[^\|]*)\|
(?<nrofitems>[^\|]*)\|
(?<items>(?:[^\|]{0,5}\|){1,}))*$";
        Regex myRegex = new Regex(strRegex, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
        string strTargetString = @"446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0||";

        MatchCollection matches = myRegex.Matches(strTargetString);

        foreach (Match m in matches)
        {
            for (int idx = 0; idx < m.Groups["grp"].Captures.Count; idx++)
            {
                Console.WriteLine("Group:");
                Console.WriteLine($"\tmachinenr={m.Group["machinenr"].Captures[idx]}");
                Console.WriteLine($"\tcontroldone={m.Groups["controldone"].Captures[idx]}");
                Console.WriteLine($"\tnrofitems={m.Groups["nrofitems"].Captures[idx]}");
                Console.WriteLine($"\titems={m.Groups["items"].Captures[idx]}");
            }
        }
    }
}

enter image description here

使用C#IEnumerable&lt; T&gt;算法

分割字符串并解析后续数组似乎更容易。但是,如果您担心处理大字符串或者不想使用String.Split(),则可以使用IEnumerable<T>方法。这是一种方法......

class Program
{

    public class Entry
    {
        public string MachineNr { get; set; }
        public string ControlDone { get; set; }
        public int Count { get; set; }
        public List<string> Items { get; set; }

        private static IEnumerable<string> fields(string list)
        {
            int idx = 0;
            do
            {
                int ndx = list.IndexOf('|', idx);
                if (ndx == 1)
                    yield return list.Substring(idx);
                else
                    yield return list.Substring(idx, ndx - idx);                        

                idx = ++ndx;
            }
            while (idx > 0 && idx < list.Length-1) ;
        }

        public static IEnumerable<Entry> parseList(string list)
        {
            int idx =0;
            var fields = Entry.fields(list).GetEnumerator();
            while (fields.MoveNext())
            {
                var e = new Entry();
                e.MachineNr = fields.Current;
                if (fields.MoveNext())
                {
                    e.ControlDone = fields.Current;
                    if (fields.MoveNext())
                    {
                        int val = 0;
                        e.Count = int.TryParse(fields.Current, out val) ? val : 0;
                        e.Items = new List<string>();
                        for (int x=e.Count;x>0;x--)
                        {
                            if (fields.MoveNext())
                                e.Items.Add(fields.Current);
                        }
                    }
                }

                yield return e;
            }
        }
    }
    static void Main(string[] args)
    {
        string strTargetString = @"446408|0|1|111|446408|0|3|99884|111|73732|446408|0|0||";
        foreach (var entry in Entry.parseList(strTargetString))
        {
            Console.WriteLine(
$@"Group:
    Machine:        {entry.MachineNr}
    ControlDone:    {entry.ControlDone}
    Count:          {entry.Count}
    Items:          {string.Join(", ",entry.Items)}");
        }

    }
}