Использование регулярных выражений для извлечения групп чисел из строки

Мне нужно преобразовать строку, например,

"[1,2,3,4][5,6,7,8]"

на группы целых чисел, скорректированных так, чтобы они основывались на нуле, а не на единице:

{0,1,2,3} {4,5,6,7}

Также применяются следующие правила:

  • Строка должна содержать хотя бы 1 группу чисел, заключенных в квадратные скобки.
  • В каждой группе должно быть не менее 2 номеров.
  • Каждое число должно быть уникальным (это не то, чего я пытаюсь добиться с помощью регулярного выражения).
  • 0 недействителен, но 10, 100 и т.д.

Поскольку у меня нет опыта работы с регулярными выражениями, сейчас я использую два;

@"^(?:\[(?:[1-9]+[\d]*,)+(?:[1-9]+[\d]*){1}\])+$";

и

@"\[(?:[1-9]+[\d]*,)+(?:[1-9]+[\d]*){1}\]";

Я использую первый для проверки ввода, а второй — для получения всех совпадений с набором чисел в квадратных скобках.

Затем я использую манипуляции со строками .Net, чтобы обрезать квадратные скобки и извлечь числа, разобрать их и вычесть 1, чтобы получить нужный мне результат.

Мне было интересно, смогу ли я улучшить цифры, используя захваты, но не уверен, как они работают.


Окончательное решение:

В конце концов я использовал следующее регулярное выражение для проверки входной строки

@"^(?<set>\[(?:[1-9]\d{0,7}(?:]|,(?=\d))){2,})+$"

Шаблон agent-j подходит для сбора необходимой информации, но также соответствует строке типа «[1,2,3,4][5]» и потребует от меня дополнительной фильтрации результатов.

Я получаю доступ к захватам через именованную группу «набор» и использую второе простое регулярное выражение для извлечения чисел.

'[1-9]\d{0,7}' упрощает синтаксический анализ целых чисел, ограничивая числа до 99 999 999 и избегая исключений переполнения.

MatchCollection matches = new Regex(@"^(?<set>\[(?:[1-9]\d{0,7}(?:]|,(?=\d))){2,})+$").Matches(inputText);

if (matches.Count != 1)return;

CaptureCollection captures = matches[0].Groups["set"].Captures;

var resultJArray = new int[captures.Count][];
var numbersRegex =  new Regex(@"\d+");
for (int captureIndex = 0; captureIndex < captures.Count; captureIndex++)
{
    string capture = captures[captureIndex].Value;
    MatchCollection numberMatches = numbersRegex.Matches(capture);
    resultJArray [captureIndex] = new int[numberMatches.Count];
    for (int numberMatchIndex = 0; numberMatchIndex < numberMatches.Count; numberMatchIndex++)
    {
        string number = numberMatches[numberMatchIndex].Value;
        int numberAdjustedToZeroBase = Int32.Parse(number) - 1;
        resultJArray [captureIndex][numberMatchIndex] = numberAdjustedToZeroBase;
    }
}

person Grokodile    schedule 24.06.2011    source источник
comment
Вы не можете просто использовать [1-9]+ или я неправильно понял ваш вопрос?   -  person Dve    schedule 24.06.2011
comment
@Dve, числа могут быть 10, 100, 1000 ... только не 0.   -  person Grokodile    schedule 24.06.2011
comment
В этом случае недостаточно [1-9][0-9]*   -  person Dve    schedule 24.06.2011


Ответы (3)


string input = "[1,2,3,4][5,6,7,8][534,63433,73434,8343434]";
string pattern = @"\G(?:\[(?:(\d+)(?:,|(?=\]))){2,}\])";//\])+$";
MatchCollection matches = Regex.Matches (input, pattern);

Начнем с того, что любой (regex) с простыми круглыми скобками является группой захвата. Это означает, что механизм регулярных выражений будет захватывать (сохранять позиции, соответствующие этой группе). Чтобы этого избежать (когда вам это не нужно, используйте (?:regex). Я сделал это выше.

Индекс 0 является особым и означает всего родителя. т.е. match.Groups[0].Value всегда совпадает с match.Value и match.Groups[0].Captures[0].Value. Таким образом, вы можете считать, что коллекции Groups и Capture начинаются с индекса 1.

Как вы можете видеть ниже, каждое совпадение содержит группу цифр в квадратных скобках. Вы захотите использовать захваты 1-n из группы 1 каждого матча.

foreach (Match match in matches)
{
   // [1,2]
   // use captures 1-n from the first group.
   for (int i = 1; i < match.Group[1].Captures.Count; i++)
   {
      int number = int.Parse(match.Group[1].Captures[i]);
      if (number == 0)
         throw new Exception ("Cannot be 0.");
   }
}

Match[0] => [1,2,3,4]
  Group[0] => [1,2,3,4]
    Capture[0] => [1,2,3,4]
  Group[1] => 4
    Capture[0] => 1
    Capture[1] => 2
    Capture[2] => 3
    Capture[3] => 4
Match[1] => [5,6,7,8]
  Group[0] => [5,6,7,8]
    Capture[0] => [5,6,7,8]
  Group[1] => 8
    Capture[0] => 5
    Capture[1] => 6
    Capture[2] => 7
    Capture[3] => 8
Match[2] => [534,63433,73434,8343434]
  Group[0] => [534,63433,73434,8343434]
    Capture[0] => [534,63433,73434,8343434]
  Group[1] => 8343434
    Capture[0] => 534
    Capture[1] => 63433
    Capture[2] => 73434
    Capture[3] => 8343434

\G заставляет совпадение начинаться с начала последнего совпадения (поэтому вы не найдете совпадение [1,2] [3,4]). {2,} удовлетворяет вашему требованию, чтобы в каждом совпадении было как минимум 2 числа.

Выражение будет совпадать, даже если есть 0. Я предлагаю вам добавить эту проверку к другим материалам, не связанным с регулярными выражениями. Это сделает регулярное выражение проще.

person agent-j    schedule 24.06.2011

Следующее регулярное выражение будет проверять, а также выделять группы совпадений группы [] в квадратных скобках, а также внутри нее, каждое число

(?:([1-9][0-9]*)\,?){2,}



[1][5]  -  fail
[1]  -  fail
[]  -  fail
[a,b,c][5]  -  fail
[1,2,3,4]  -  pass
[1,2,3,4,5,6,7,8][5,6,7,8]  -  pass
[1,2,3,4][5,6,7,8][534,63433,73434,8343434]  -  pass
person Dve    schedule 24.06.2011
comment
Это гораздо более чистое выражение для части сопоставления чисел, единственная проблема с ним заключается в том, что оно соответствует 1,2,3 из [0,1,2,3], но мне нужно исключить всю группу, если она содержит 0, и, следовательно, также сделать недействительным вся входная строка, т.е. [0,1,2,3][4,5,6,7] должна завершиться ошибкой, даже если вторая группа является проходной. - person Grokodile; 24.06.2011

Как насчет \d+ и глобального флага?

person aercolino    schedule 24.06.2011
comment
по причинам, которые я не вправе раскрывать ;) 0 сам по себе не допускается во входных данных, следовательно, [1-9]+[\d]*. Что такое глобальный флаг? - person Grokodile; 24.06.2011