Символ регулярного выражения, используемый в качестве разделителя в подгруппах - PullRequest
0 голосов
/ 19 мая 2018

У меня есть строка, разделенная символом трубы.Это повторяемая последовательность:

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

Однако там, где вы видите тег items, у вас будут номера предметов, разделенные символом трубы между ними.Ну, это не умный формат, но я должен решить его, и я хочу сделать с регулярным выражением в C #.Таким образом, предполагая, что приведенный выше формат дает реальный пример:

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

Обратите внимание, что теоретически не должно быть значения между каналами, а также содержимое не ограничено длиной.Идентификатор элемента может быть 111 или 877333, но даже смешанный буквенно-цифровой идентификатор XB111.Итак, у нас есть две машины без предметов:

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

Здесь у нас есть несколько машин без или с некоторыми предметами.Обратите внимание, что символ трубы также используется для разделения элементов, поэтому у вас есть трубы внутри труб:

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

В этом аппарате есть три элемента: 446408 | 0 | 3 | 99884 | 111 | 73732 |

Идентификатор предмета:

99884|111|73732

Как должно выглядеть регулярное выражение?Я пробовал с именованными ниже группами (легче читать), но это просто не работает:

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

Вот пояснение для @Atterson @sln и @.Обратите внимание, что количество предметов может быть 0-н, нет ограничений на количество.Давайте возьмем этот пример, длинную строку с машинами и их элементами: 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 ||Итак, чтобы взять пример значений, на которые каждая часть должна быть разбита, давайте используем второе совпадение / часть.Это машина с номером 446408, она не контролируется 0, у нее есть 3 позиции, идентификаторы: 99884 | 111 | 73732.После этих пунктов может следовать новая последовательность:

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

.@Sanxofon, пожалуйста, проверьте свое регулярное выражение здесь: [ссылка] https://regex101.com/r/kC3gH0/87, и вы увидите, к сожалению, оно не соответствует.

Ответы [ 2 ]

0 голосов
/ 20 мая 2018

Именованные группы захвата (?<nam>...) не (?P<name>...) в C #.Кроме того, вы выразили желание иметь повторяющиеся совпадения (поэтому я завернул ваше регулярное выражение в повторяющийся (?<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 Алгоритм

Казалось бы, проще просто разбить строку иРазобрать последующий массив. Но, если вы беспокоитесь о работе с большими строками или не хотите использовать 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)}");
        }

    }
}
0 голосов
/ 20 мая 2018

Это невозможно решить с помощью регулярного выражения, и нет никакого способа сказать регулярному выражению что-то вроде: "Совпадение .*?\| столько же раз, сколько у определенной группы захвата ... которая содержит число".Это простое решение этой проблемы с использованием старого 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||";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...