Чтение Python Regex в комментариях стиля c

Я пытаюсь найти комментарии стиля c в файле ac, но им не хватает, если внутри котировок есть //. Это файл:

/*My function is great.*/ int j = 0//hello world void foo(){ //tricky example cout << "This // is // not a comment\n"; } 

он будет соответствовать этому cout. Это то, что я до сих пор (я могу совместить / ** / комментарии уже)

 fp = open(s) p = re.compile(r'//(.+)') txt = p.findall(fp.read()) print (txt) 

  • Захват названных групп в regex с re.findall
  • Как сжато каскадировать несколько операторов regex в Python
  • Выполнить булевскую арифметику, включая круглые скобки с регулярным выражением?
  • Повторное совпадение совпадений
  • Как заменить N-й вид иглы в стоге сена? (Python)
  • Греп и Питон
  • Регулярное выражение Python для повторения нескольких совпадений
  • Извлечь строку с Python re.match
  • 2 Solutions collect form web for “Чтение Python Regex в комментариях стиля c”

    Первым шагом является определение случаев, когда // или /* не должны интерпретироваться как начало подстроки комментариев. Например, когда они находятся внутри строки (между кавычками) . Чтобы избежать содержимого между кавычками (или другими вещами), трюк состоит в том, чтобы поместить их в группу захвата и вставить ссылку в шаблон замены:

    шаблон:

     ("(?:[^"\\]|\\[\s\S])*"|'(?:[^'\\]|\\[\s\S])*')|//.*|/\*(?:[^*]|\*(?!/))*\*/ 

    замена:

     \1 

    онлайн-демонстрация

    Поскольку цитируемые части сначала ищут, каждый раз, когда вы находите // или /*...*/ , вы можете быть уверены, что вы не внутри строки.

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

     ("(?=((?:[^"\\]+|\\[\s\S])*))\2"|'(?=((?:[^'\\]+|\\[\s\S])*))\3')|//.*|/\*(?=((?:[^*]+|\*(?!/))*))\4\*/ 

    (?=(something+))\1 – это всего лишь способ подражать атомной группе (?>something+)

    онлайн-демонстрация

    Итак, если вы хотите только найти комментарии (и не удалять их), наиболее удобно поместить комментарии в шаблон в группу захвата и проверить, не является ли он пустым. Следующий шаблон был изменен (после комментария Джонатана Леффлера) для обработки триграфа ??/ который интерпретируется как символ обратной косой черты препроцессором (я предполагаю, что код не написан для опции -trigraphs ) и обрабатывать обратную косую черту за которым следует символ новой строки, который позволяет отформатировать одну строку на нескольких строках:

     fp = open(s) p = re.compile(r'''(?x) (?=["'/]) # trick to make it faster, a kind of anchor (?: "(?=((?:[^"\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\1" # double quotes string | '(?=((?:[^'\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\2' # single quotes string | ( /(?:(?:\?\?/|\\)\n)*/(?:.*(?:\?\?|\\)/\n)*.* # single line comment | /(?:(?:\?\?/|\\)\n)*\* # multiline comment (?=((?:[^*]+|\*+(?!(?:(?:\?\?/|\\)\n)*/))*))\4 \*(?:(?:\?\?/|\\)\n)*/ ) ) ''') for m in p.findall(fp.read()): if (m[2]): print m[2] 

    Эти изменения не повлияют на эффективность шаблонов, так как основная работа для механизма регулярных выражений заключается в поиске позиций, начинающихся с котировки или косой черты. Эта задача упрощается благодаря наличию lookahead в начале шаблона (?=["'/]) , Который позволяет внутренним оптимизациям быстро находить первый символ.

    Другая оптимизация – использование эмулируемых атомных групп, что сводит назад к минимуму и позволяет использовать жадные кванторы внутри повторяющихся групп.

    NB: вероятность отсутствия синтаксиса heredoc в C!

    Метод re.findall Python в основном работает так же, как и большинство лексеров: он последовательно возвращает самый длинный матч, начиная с предыдущего окончания. Все, что требуется, – создать дизъюнкцию всех лексических моделей:

     (<pattern 1>)|(<pattern 2>)|...|(<pattern n>) 

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

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

     (<pattern 1>)|(?:<unimportant pattern 2>)|(<pattern 3) 

    Имея это в виду, давайте посмотрим, как tokenize C достаточно, чтобы распознавать комментарии. Нам нужно иметь дело с:

    1. Однострочные комментарии: // Comment
    2. Многострочные комментарии: /* Comment */
    3. Строка с двойными кавычками: "Might include escapes like \n"
    4. Одиночный кавычек: '\t'
    5. (См. Ниже еще несколько раздражающих случаев)

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

    1. Две слэши, за которыми следует что-то другое, кроме новой строки: //[^\n]*
    2. Это регулярное выражение утомительно, чтобы объяснить: /*[^*]*[*]+(?:[^/*][^*]*[*]+)*/ Обратите внимание, что он использует (?:...) – избегайте захвата повторяющейся группы.
    3. Цитата, любое повторение символа, кроме цитаты и обратной косой черты, или обратная косая черта, за которой следует какой-либо символ. Это не точное определение escape-последовательности, но это достаточно хорошо, чтобы обнаружить, когда « завершает строку, и это все, о чем мы заботимся»: "(?:[^"\\]|\\.*)"
    4. То же, что и (3), но с одинарными кавычками: '(?:[^'\\]|\\.)*'

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

     p = re.compile('|'.join((r"(//[^\n])*" ,r"/*[^*]*[*]+(?:[^/*][^*]*[*]+)*/" ,'"'+r"""(?:[^"\\]|\\.)*"""+'"' ,r"'(?:[^'\\]|\\.)*'"))) return [c[2:] for c in p.findall(text) if c] 

    Выше я оставил некоторые неясные случаи, которые вряд ли возникнут:

    1. В директиве #include <...> <...> по существу является строкой. Теоретически он может содержать кавычки или последовательности, которые выглядят как комментарии, но на практике вы никогда не увидите:

       #include </*This looks like a comment but it is a filename*/> 
    2. Линия, заканчивающаяся символом \ , продолжается на следующей строке; символ \ и следующий символ новой строки просто удаляются из ввода. Это происходит до того, как выполняется любое лексическое сканирование, так что это совершенно законный комментарий (на самом деле два комментария):

       /\ **************** Surprise! **************\ ////////////////////////////////////////// 
    3. Чтобы сделать это хуже, тригр ??/ такой же, как и \ , и эта замена происходит перед обработкой продолжения.

       /************************************//??/ **************** Surprise! ************??/ ////////////////////////////////////////// 

      Вне конкурсов обфускации никто не использует триграфы. Но они все еще в стандарте. Самый простой способ справиться с обеими этими проблемами – это предварительная проверка строки:

       return [c[2:] for c in p.findall(text.replace('//?','\\').replace('\\\n','')) if c] 

    Единственный способ справиться с проблемой #include <...> , если вы действительно позаботились об этом, – добавить еще один шаблон, что-то вроде #define\s*<[^>\n]*> .

    Python - лучший язык программирования в мире.