Шаблон регулярного выражения для сопоставления, исключая когда... / За исключением

- Редактировать-- В текущих ответах есть некоторые полезные идеи, но я хочу что-то более полное, что я могу на 100% понять и использовать повторно; Вот почему я назначил награду. Также идеи, которые работают везде, лучше для меня, чем не стандартный синтаксис, такой как \K

Этот вопрос о том, как я могу сопоставить шаблон, за исключением некоторых ситуаций s1 s2 s3. Я привожу конкретный пример, чтобы показать свое значение, но предпочитаю общий ответ, который я могу понять на 100%, чтобы использовать его в других ситуациях.

пример

Я хочу сопоставить пять цифр, используя \b\d{5}\b но не в трех ситуациях s1 s2 s3:

s1: не в строке, заканчивающейся точкой, как в этом предложении.

s2: нигде внутри паренов.

s3: не внутри блока, который начинается с if( и заканчивается //endif

Я знаю, как решить любую из s1, s2, s3 с предвкушением и взглядом назад, особенно в C# взглядом назад или \K в PHP.

Например

s1 (?m)(?!\d+.*?\.$)\d+

S3 с C# смотреть назад (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

s3 с PHP \K (?:(?:if\(.*?//endif)\D*)*\K\d+

Но сочетание условий заставляет мою голову взорваться. Еще более плохая новость заключается в том, что мне может понадобиться добавить другие условия s4 s5 в другое время.

Хорошая новость в том, что мне все равно, обрабатывать ли файлы с использованием наиболее распространенных языков, таких как PHP, C#, Python или стиральная машина моего соседа.:) Я довольно новичок в Python & Java, но мне интересно узнать, есть ли у него решение.

Так что я пришел сюда, чтобы посмотреть, если кто-то думает о гибком рецепте.

Подсказки в порядке: вам не нужно давать мне полный код.:)

Спасибо.

6 ответов

Решение

Ганс, я возьму наживку и уточню свой предыдущий ответ. Вы сказали, что хотите "что-то более полное", поэтому я надеюсь, что вы не будете возражать против длинного ответа - просто пытаться угодить. Давайте начнем с некоторого фона.

Во-первых, это отличный вопрос. Часто возникают вопросы о сопоставлении определенных шаблонов, за исключением определенных контекстов (например, внутри блока кода или внутри скобок). Эти вопросы часто приводят к довольно неловким решениям. Так что ваш вопрос о множественных контекстах - это особый вызов.

сюрприз

Удивительно, но есть по крайней мере одно эффективное решение, общее, простое в реализации и приятное в обслуживании. Он работает со всеми разновидностями регулярных выражений, которые позволяют вам проверять группы захвата в вашем коде. И это случается, чтобы ответить на ряд общих вопросов, которые на первый взгляд могут звучать иначе, чем у вас: "соответствовать всем, кроме пончиков", "заменить все, кроме...", "соответствовать всем словам, кроме тех, которые находятся в черном списке моей мамы", "игнорировать теги "," соответствуют температуре, если они не выделены курсивом "...

К сожалению, методика не очень хорошо известна: по моим оценкам, в двадцати SO-вопросах, которые могли бы ее использовать, только один имеет один ответ, в котором упоминается об этом, а это, возможно, один из пятидесяти или шестидесяти ответов. Смотрите мой обмен с Коби в комментариях. Техника подробно описана в этой статье, которая (с оптимизмом) называет ее "лучшим из когда-либо описанных регулярных выражений". Не вдаваясь в подробности, я попытаюсь дать вам четкое представление о том, как работает техника. Для более подробной информации и примеров кода на разных языках я советую вам обратиться к этому ресурсу.

Лучше известное изменение

Существует вариант с использованием синтаксиса, специфичного для Perl и PHP, который выполняет то же самое. Вы увидите это на SO в руках мастеров регулярных выражений, таких как HamZa и HamZa. Я расскажу вам больше об этом ниже, но я сосредоточусь здесь на общем решении, которое работает со всеми разновидностями регулярных выражений (при условии, что вы можете проверять группы захвата в своем коде).

Спасибо за весь фон, zx81... Но какой рецепт?

Ключевой факт

Метод возвращает совпадение в захвате группы 1. Это вообще не волнует общий матч.

Фактически, хитрость заключается в том, чтобы соответствовать различным контекстам, которые мы не хотим (объединяя эти контексты, используя | ИЛИ / чередование), чтобы "нейтрализовать их". После сопоставления всех нежелательных контекстов последняя часть чередования совпадает с тем, что мы действительно хотим, и записывает его в группу 1.

Общий рецепт

Not_this_context|Not_this_either|StayAway|(WhatYouWant)

Это будет соответствовать Not_this_context, но в некотором смысле это совпадение входит в мусорное ведро, потому что мы не будем смотреть на общие совпадения: мы рассматриваем только захваты группы 1.

В вашем случае, игнорируя ваши цифры и три контекста, мы можем сделать:

s1|s2|s3|(\b\d+\b)

Обратите внимание, что, поскольку мы фактически сопоставляем s1, s2 и s3 вместо того, чтобы пытаться избежать их с помощью обходных путей, отдельные выражения для s1, s2 и s3 могут оставаться ясными как день. (Они являются подвыражениями на каждой стороне |)

Все выражение может быть написано так:

(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)

Посмотрите эту демонстрацию (но обратите внимание на группы захвата в нижней правой панели.)

Если вы мысленно пытаетесь разделить это регулярное выражение на каждом | разделитель, это на самом деле только серия из четырех очень простых выражений.

Для ароматов, которые поддерживают свободное пространство, это выглядит особенно хорошо.

(?mx)
      ### s1: Match line that ends with a period ###
^.*\.$  
|     ### OR s2: Match anything between parentheses ###
\([^\)]*\)  
|     ### OR s3: Match any if(...//endif block ###
if\(.*?//endif  
|     ### OR capture digits to Group 1 ###
(\b\d+\b)

Это исключительно легко читать и поддерживать.

Расширяя регулярное выражение

Если вы хотите игнорировать больше ситуаций s4 и s5, вы добавляете их в более чередующиеся слева:

s4|s5|s1|s2|s3|(\b\d+\b)

Как это работает?

Нежелательные контексты добавляются в список чередований слева: они будут совпадать, но эти общие совпадения никогда не проверяются, поэтому сопоставление их - это способ поместить их в "мусорную корзину".

Тем не менее, содержимое, которое вы хотите, записывается в группу 1. Затем вам нужно программно проверить, что группа 1 установлена, а не пуста. Это тривиальная задача программирования (и мы позже поговорим о том, как это делается), особенно учитывая, что она оставляет вам простое регулярное выражение, которое вы можете сразу понять, а затем пересмотреть или дополнить по мере необходимости.

Я не всегда фанат визуализаций, но этот хорошо показывает, насколько простой метод. Каждая "строка" соответствует потенциальному совпадению, но только нижняя строка включена в Группу 1.

Визуализация регулярных выражений

Debuggex Demo

Вариант Perl/PCRE

В отличие от общего решения, описанного выше, существует вариация для Perl и PCRE, которая часто наблюдается на SO, по крайней мере, в руках богов-регулярников, таких как @CasimiretHippolyte и @HamZa. Это:

(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant

В твоем случае:

(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b

Этот вариант немного проще в использовании, потому что содержимое, сопоставленное в контекстах s1, s2 и s3, просто пропускается, поэтому вам не нужно проверять захваты группы 1 (обратите внимание, что скобки пропали). Спички содержат только whatYouWant

Обратите внимание, что (*F), (*FAIL) а также (?!) все одно и то же. Если вы хотите быть более неясным, вы можете использовать (*SKIP)(?!)

демо для этой версии

Приложения

Вот некоторые общие проблемы, которые этот метод часто может легко решить. Вы заметите, что выбор слова может заставить некоторые из этих проблем звучать по-разному, хотя на самом деле они практически идентичны.

  1. Как я могу сопоставить foo, кроме как где-нибудь в теге, как <a stuff...>...</a>?
  2. Как я могу соответствовать Foo, кроме как в <i> тег или фрагмент Ja vaScript (больше условий)?
  3. Как я могу сопоставить все слова, которых нет в этом черном списке?
  4. Как я могу игнорировать что-либо внутри блока SUB... END SUB?
  5. Как я могу сопоставить все, кроме... s1 s2 s3?

Как запрограммировать захваты группы 1

Вы не относились к коду, но, для завершения... Код для проверки группы 1, очевидно, будет зависеть от вашего языка. В любом случае, он не должен добавлять более пары строк в код, который вы бы использовали для проверки совпадений.

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

альтернативы

В зависимости от сложности вопроса и используемого механизма регулярных выражений, существует несколько альтернатив. Вот два из них, которые могут применяться к большинству ситуаций, включая множественные условия. На мой взгляд, ни один из них не так привлекателен, как s1|s2|s3|(whatYouWant) рецепт, хотя бы потому, что ясность всегда побеждает.

1. Замените затем Матч.

Хорошее решение, которое звучит смешно, но хорошо работает во многих средах, - это работать в два этапа. Первое регулярное выражение нейтрализует контекст, который вы хотите игнорировать, заменяя потенциально конфликтующие строки. Если вы хотите только сопоставить, то вы можете заменить его пустой строкой, а затем выполнить сопоставление на втором шаге. Если вы хотите заменить, вы можете сначала заменить игнорируемые строки чем-то отличительным, например, окружив ваши цифры цепочкой фиксированной ширины: @@@, После этой замены вы можете заменить то, что вы действительно хотели, тогда вам придется вернуть свой отличительный @@@ строки.

2. Lookarounds.

Ваш оригинальный пост показал, что вы понимаете, как исключить одно условие, используя обходные пути. Вы сказали, что C# отлично подходит для этого, и вы правы, но это не единственный вариант..NET регулярные выражения, например, в C#, VB.NET и Visual C++, а также все еще экспериментальный regex модуль для замены re в Python есть только два известных мне движка, которые поддерживают просмотр бесконечной ширины. С помощью этих инструментов одно условие в одном взгляде может позаботиться о том, чтобы смотреть не только за спиной, но и за матчем и за его пределами, избегая необходимости координировать свои действия с прогнозом. Больше условий? Больше взглядов.

Повторно использовав регулярное выражение для s3 в C#, весь шаблон будет выглядеть следующим образом.

(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

Но теперь вы знаете, что я не рекомендую это, верно?

Пропуски

@HamZa и @Jerry предложили упомянуть дополнительный прием для случаев, когда вы пытаетесь просто удалить WhatYouWant, Вы помните, что рецепт, чтобы соответствовать WhatYouWant (захват его в группу 1) был s1|s2|s3|(WhatYouWant), право? Удалить все экземпляры WhatYouWant, вы измените регулярное выражение на

(s1|s2|s3)|WhatYouWant

Для замены строки вы используете $1, Здесь происходит то, что для каждого экземпляра s1|s2|s3 что подобрано, замена $1 заменяет этот экземпляр самим собой (ссылается на $1). С другой стороны, когда WhatYouWant сопоставляется, заменяется пустой группой и ничем иным - и, следовательно, удаляется. Посмотрите эту демонстрацию, спасибо @HamZa и @Jerry за предложение этого замечательного дополнения.

Замены

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

  1. При замене на ничто, см. Трюк "Удаление" выше.
  2. При замене, если используется Perl или PCRE, используйте (*SKIP)(*F) упомянутый выше вариант, чтобы точно соответствовать тому, что вы хотите, и сделать прямую замену.
  3. В других вариантах в вызове функции замены проверьте соответствие с помощью обратного вызова или лямбда-выражения и замените, если установлена ​​группа 1. Если вам нужна помощь в этом, статья, на которую вы ссылаетесь, даст вам код на разных языках.

Повеселись!

Нет, подождите, это еще не все!

Ах, нет, я сохраню это для моих мемуаров в двадцати томах, которые будут выпущены следующей весной.

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

РЕДАКТИРОВАТЬ: позвольте мне немного расширить, потому что вопрос стал более интересным:-)

Общая идея, которую вы пытаетесь уловить, заключается в сопоставлении с определенным шаблоном регулярных выражений, но не в тех случаях, когда в тестовой строке присутствуют определенные другие (могут быть любые) шаблоны. К счастью, вы можете воспользоваться вашим языком программирования: упростите регулярные выражения и просто используйте сложное условие. Лучше всего было бы зафиксировать эту идею в повторно используемом компоненте, поэтому давайте создадим класс и метод, который ее реализует:

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class MatcherWithExceptions {
  private string m_searchStr;
  private Regex m_searchRegex;
  private IEnumerable<Regex> m_exceptionRegexes;

  public string SearchString {
    get { return m_searchStr; }
    set {
      m_searchStr = value;
      m_searchRegex = new Regex(value);
    }
  }

  public string[] ExceptionStrings {
    set { m_exceptionRegexes = from es in value select new Regex(es); }
  }

  public bool IsMatch(string testStr) {
    return (
      m_searchRegex.IsMatch(testStr)
      && !m_exceptionRegexes.Any(er => er.IsMatch(testStr))
    );
  }
}

public class App {
  public static void Main() {
    var mwe = new MatcherWithExceptions();

    // Set up the matcher object.
    mwe.SearchString = @"\b\d{5}\b";
    mwe.ExceptionStrings = new string[] {
      @"\.$"
    , @"\(.*" + mwe.SearchString + @".*\)"
    , @"if\(.*" + mwe.SearchString + @".*//endif"
    };

    var testStrs = new string[] {
      "1." // False
    , "11111." // False
    , "(11111)" // False
    , "if(11111//endif" // False
    , "if(11111" // True
    , "11111" // True
    };

    // Perform the tests.
    foreach (var ts in testStrs) {
      System.Console.WriteLine(mwe.IsMatch(ts));
    }
  }
}

Итак, выше, мы устанавливаем строку поиска (пять цифр), несколько строк исключений (ваши s1, s2 и s3), а затем пытаемся сопоставить несколько тестовых строк. Результаты печати должны быть такими, как показано в комментариях рядом с каждой тестовой строкой.

Ганс, если вы не возражаете, я использовал стиральную машину вашего соседа под названием Perl:)

Отредактировано: ниже псевдокода:

  loop through input
  if line contains 'if(' set skip=true
        if skip= true do nothing
        else
           if line match '\b\d{5}\b' set s0=true
           if line does not match s1 condition  set s1=true
           if line does not match s2 condition  set s2=true
           if s0,s1,s2 are true print line 
  if line contains '//endif' set skip=false

Учитывая файл input.txt:

tiago@dell:~$ cat input.txt 
this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

И скрипт validator.pl:

tiago@dell:~$ cat validator.pl 
#! /usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

sub validate_s0 {
    my $line = $_[0];
    if ( $line =~ \d{5/ ){
        return "true";
    }
    return "false";
}

sub validate_s1 {
    my $line = $_[0];
    if ( $line =~ /\.$/ ){
        return "false";
    }
    return "true";
}

sub validate_s2 {
    my $line = $_[0];
    if ( $line =~ /.*?\(.*\d{5.*?\).*/ ){
        return "false";
    }
    return "true";
}

my $skip = "false";
while (<>){
    my $line = $_; 

    if( $line =~ /if\(/ ){
       $skip = "true";  
    }

    if ( $skip eq "false" ) {
        my $s0_status = validate_s0 "$line"; 
        my $s1_status = validate_s1 "$line";
        my $s2_status = validate_s2 "$line";

        if ( $s0_status eq "true"){
            if ( $s1_status eq "true"){
                if ( $s2_status eq "true"){
                    print "$line";
                }
            }
        }
    } 

    if ( $line =~ /\/\/endif/) {
        $skip="false";
    }
}

Исполнение:

tiago @ dell: ~ $ cat input.txt | Perl validator.pl 
должно соответствовать 12345
должно соответствовать 12345
должно соответствовать 12345

Не уверен, поможет ли это вам или нет, но я предоставляю решение с учетом следующих предположений:

  1. Вам нужно элегантное решение, чтобы проверить все условия
  2. Условия могут измениться в будущем и в любое время.
  3. Одно условие не должно зависеть от других.

Однако я учел и следующее -

  1. Данный файл имеет минимальные ошибки в нем. Если это так, то мой код может потребовать некоторых модификаций, чтобы справиться с этим.
  2. Я использовал Stack, чтобы отслеживать if( блоки.

Хорошо, вот решение -

Я использовал C# и вместе с ним MEF (Microsoft Extensibility Framework) для реализации настраиваемых парсеров. Идея состоит в том, чтобы использовать один синтаксический анализатор для анализа и список настраиваемых классов валидатора для проверки строки и возврата значения true или false на основе проверки. Затем вы можете добавить или удалить любой валидатор в любое время или добавить новые, если хотите. До сих пор я уже реализовал для S1, S2 и S3, о которых вы упомянули, проверьте классы в пункте 3. Вы должны добавить классы для s4, s5, если вам это нужно в будущем.

  1. Сначала создайте интерфейсы -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.Contracts
    {
        public interface IParser
        {
            String[] GetMatchedLines(String filename);
        }
    
        public interface IPatternMatcher
        {
            Boolean IsMatched(String line, Stack<string> stack);
        }
    }
    
  2. Затем идет программа для чтения файлов и проверки -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using FileParserDemo.Contracts;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Collections;
    
    namespace FileParserDemo.Parsers
    {
        public class Parser : IParser
        {
            [ImportMany]
            IEnumerable<Lazy<IPatternMatcher>> parsers;
            private CompositionContainer _container;
    
            public void ComposeParts()
            {
                var catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(IParser).Assembly));
                _container = new CompositionContainer(catalog);
                try
                {
                    this._container.ComposeParts(this);
                }
                catch
                {
    
                }
            }
    
            public String[] GetMatchedLines(String filename)
            {
                var matched = new List<String>();
                var stack = new Stack<string>();
                using (StreamReader sr = File.OpenText(filename))
                {
                    String line = "";
                    while (!sr.EndOfStream)
                    {
                        line = sr.ReadLine();
                        var m = true;
                        foreach(var matcher in this.parsers){
                            m = m && matcher.Value.IsMatched(line, stack);
                        }
                        if (m)
                        {
                            matched.Add(line);
                        }
                     }
                }
                return matched.ToArray();
            }
        }
    }
    
  3. Затем идет реализация отдельных контролеров, имена классов говорят сами за себя, поэтому я не думаю, что им нужно больше описаний.

    using FileParserDemo.Contracts;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.PatternMatchers
    {
        [Export(typeof(IPatternMatcher))]
        public class MatchAllNumbers : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\d+");
                return regex.IsMatch(line);
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveIfBlock : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("if\\(");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        //push the if
                        stack.Push(m.ToString());
                    }
                    //ignore current line, and will validate on next line with stack
                    return true;
                }
                regex = new Regex("//endif");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        stack.Pop();
                    }
                }
                return stack.Count == 0; //if stack has an item then ignoring this block
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithEndPeriod : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("(?m)(?!\\d+.*?\\.$)\\d+");
                return regex.IsMatch(line);
            }
        }
    
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithInParenthesis : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\(.*\\d+.*\\)");
                return !regex.IsMatch(line);
            }
        }
    }
    
  4. Программа -

    using FileParserDemo.Contracts;
    using FileParserDemo.Parsers;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var parser = new Parser();
                parser.ComposeParts();
                var matches = parser.GetMatchedLines(Path.GetFullPath("test.txt"));
                foreach (var s in matches)
                {
                    Console.WriteLine(s);
                }
                Console.ReadLine();
            }
        }
    }
    

Для тестирования я взял образец файла @Tiago как Test.txt который имел следующие строки -

this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

Дает вывод -

it should match 12345
it should match 12345
it should match 12345

Не знаю, поможет ли это вам или нет, я весело провел время, играя с ним....:)

Самое приятное в этом то, что для добавления нового условия все, что вам нужно сделать, это обеспечить реализацию IPatternMatcher, он будет автоматически вызван и, следовательно, будет подтвержден.

Ваше требование, что это не внутри паренов, невозможно удовлетворить во всех случаях. А именно, если вы можете как-то найти ( влево и ) вправо, это не всегда означает, что вы находитесь внутри паренсов. Например.

(....) + 55555 + (.....) - не внутри пары, пока есть ( а также ) влево и вправо

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

((.....) + 55555 + (.....)) - внутри паренса, хотя есть закрытие ) а также ( влево и вправо.

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

Рассмотрим эту более простую задачу: с помощью регулярных выражений выясните, все ли (возможно, вложенные) парены в строке закрыты, то есть для каждого ( вам нужно найти ), Вы обнаружите, что это невозможно решить, и если вы не можете решить это с помощью регулярных выражений, то вы не сможете выяснить, находится ли слово в скобках для всех случаев, так как вы не можете найти какую-то позицию в строке, если все предшествующее ( иметь соответствующий ),

То же, что и у @zx81 (*SKIP)(*F) но с использованием отрицательного прогнозного утверждения.

(?m)(?:if\(.*?\/\/endif|\([^()]*\))(*SKIP)(*F)|\b\d+\b(?!.*\.$)

DEMO

В Python я бы легко это сделал,

import re
string = """cat 123 sat.
I like 000 not (456) though 111 is fine
222 if(  //endif if(cat==789 stuff  //endif   333"""
for line in string.split('\n'):                                  # Split the input according to the `\n` character and then iterate over the parts.
    if not line.endswith('.'):                                   # Don't consider the part which ends with a dot.
        for i in re.split(r'\([^()]*\)|if\(.*?//endif', line):   # Again split the part by brackets or if condition which endswith `//endif` and then iterate over the inner parts.
            for j in re.findall(r'\b\d+\b', i):                  # Then find all the numbers which are present inside the inner parts and then loop through the fetched numbers.
                print(j)                                         # Prints the number one ny one.

Выход:

000
111
222
333
Другие вопросы по тегам