Использование регулярных выражений для удаления комментариев из исходных файлов - PullRequest
23 голосов
/ 23 февраля 2010

Я делаю программу для автоматизации написания некоторого кода на C (я пишу для разбора строк в перечисления с тем же именем) Обработка строк в C не так уж велика. Поэтому некоторые люди пытались заставить меня попробовать Python.

Я сделал функцию, которая должна удалить C-style /* COMMENT */ и //COMMENT из строки: Вот код:

def removeComments(string):
    re.sub(re.compile("/\*.*?\*/",re.DOTALL ) ,"" ,string) # remove all occurance streamed comments (/*COMMENT */) from string
    re.sub(re.compile("//.*?\n" ) ,"" ,string) # remove all occurance singleline comments (//COMMENT\n ) from string

Итак, я попробовал этот код.

str="/* spam * spam */ eggs"
removeComments(str)
print str

И это, очевидно, ничего не сделало.

Какие-нибудь предложения относительно того, что я сделал неправильно?

Есть поговорка, которую я слышал пару раз:

Если у вас есть проблема и вы пытаетесь решить ее с помощью Regex, у вас возникают две проблемы.


EDIT: Оглядываясь на это спустя годы. (после более тщательного анализа)

Я думаю, что регулярное выражение могло быть правильным решением. И простое регулярное выражение используется здесь «достаточно хорошо». Возможно, я не подчеркнул этого достаточно в вопросе. Это было для одного конкретного файла. Это не было хитрых ситуаций. Я думаю, что было бы гораздо меньше усилий по поддержанию синтаксического анализа файла для регулярного выражения, чем для усложнения регулярного выражения в нечитаемый суп символа.

Ответы [ 10 ]

29 голосов
/ 22 августа 2013

Многие ответы уже даны, но;
как насчет "//comment-like strings inside quotes"?

ОП спрашивает, как это сделать с помощью регулярных выражений; так:

def remove_comments(string):
    pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)"
    # first group captures quoted strings (double or single)
    # second group captures comments (//single-line or /* multi-line */)
    regex = re.compile(pattern, re.MULTILINE|re.DOTALL)
    def _replacer(match):
        # if the 2nd group (capturing comments) is not None,
        # it means we have captured a non-quoted (real) comment string.
        if match.group(2) is not None:
            return "" # so we will return empty to remove the comment
        else: # otherwise, we will return the 1st group
            return match.group(1) # captured quoted-string
    return regex.sub(_replacer, string)

Это БУДЕТ удалить:

  • /* multi-line comments */
  • // single-line comments

НЕ удалить:

  • String var1 = "this is /* not a comment. */";
  • char *var2 = "this is // not a comment, either.";
  • url = 'http://not.comment.com';

Примечание : Это также будет работать для Javascript source.

28 голосов
/ 23 февраля 2010

re.sub возвращает строку, поэтому изменение вашего кода на следующее даст результаты:

def removeComments(string):
    string = re.sub(re.compile("/\*.*?\*/",re.DOTALL ) ,"" ,string) # remove all occurrences streamed comments (/*COMMENT */) from string
    string = re.sub(re.compile("//.*?\n" ) ,"" ,string) # remove all occurrence single-line comments (//COMMENT\n ) from string
    return string
16 голосов
/ 23 февраля 2010

Я бы предложил использовать РЕАЛЬНЫЙ парсер, такой как SimpleParse или PyParsing . SimpleParse требует, чтобы вы действительно знали EBNF, но очень быстро. PyParsing имеет свой собственный EBNF-подобный синтаксис, но он адаптирован для Python и позволяет легко создавать мощно точные парсеры.

Редактировать:

Вот пример того, как легко использовать PyParsing в этом контексте:

>>> test = '/* spam * spam */ eggs'
>>> import pyparsing
>>> comment = pyparsing.nestedExpr("/*", "*/").suppress()
>>> print comment.transformString(test)         
' eggs'

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

До:

/*
 * multiline comments
 * abc 2323jklj
 * this is the worst C code ever!!
*/
void
do_stuff ( int shoe, short foot ) {
    /* this is a comment
     * multiline again! 
     */
    exciting_function(whee);
} /* extraneous comment */

После того, как:

>>> print comment.transformString(code)   

void
do_stuff ( int shoe, short foot ) {

     exciting_function(whee);
} 

Он оставляет дополнительный символ новой строки, где бы он не читал комментарии, но это можно исправить.

4 голосов
/ 23 февраля 2010

Я бы порекомендовал вам прочитать эту страницу, которая содержит достаточно подробный анализ проблемы и дает хорошее представление о том, почему ваш подход не работает: http://ostermiller.org/findcomment.html

Короткая версия: регулярное выражение, которое вы ищете, это:

(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)

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

2 голосов
/ 23 февраля 2010

Вы делаете это неправильно.

Regex для Regular Languages ​​, что не является C.

1 голос
/ 24 февраля 2010
mystring="""
blah1 /* comments with
multiline */

blah2
blah3
// double slashes comments
blah4 // some junk comments

"""
for s in mystring.split("*/"):
    s=s[:s.find("/*")]
    print s[:s.find("//")]

выход

$ ./python.py

blah1


blah2
blah3
1 голос
/ 23 февраля 2010

Я вижу несколько вещей, которые вы могли бы пересмотреть.

Во-первых, Python передает объекты по значению, но некоторые типы объектов являются неизменяемыми. Строки и целые числа входят в число этих неизменных типов. Поэтому, если вы передадите строку в функцию, любые изменения в строке, которую вы сделаете внутри функции, не повлияют на строку, которую вы передали. Вместо этого вы должны попытаться вернуть строку. Кроме того, в функции removeComments () вам необходимо присвоить значение, возвращаемое re.sub (), новой переменной - как любая функция, которая принимает строку в качестве аргумента, re.sub () не будет изменять строку.

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

0 голосов
/ 21 октября 2018

Просто хочу добавить еще одно регулярное выражение, где мы должны удалить что-нибудь между * и; в питоне

data = re.sub (re.compile ("*. *? \;", Re.DOTALL), '', data)

перед * стоит косая черта, чтобы избежать метасимвола.

0 голосов
/ 22 июля 2013

Эта программа удаляет комментарии с // и / * * / из указанного файла:

#! /usr/bin/python3
import sys
import re
if len(sys.argv)!=2:
     exit("Syntax:python3 exe18.py inputfile.cc ")
else:
     print ('The following files are given by you:',sys.argv[0],sys.argv[1])
with open(sys.argv[1],'r') as ifile:
    newstring=re.sub(r'/\*.*?\*/',' ',ifile.read(),flags=re.S)
with open(sys.argv[1],'w') as ifile:
    ifile.write(newstring)
print('/* */ have been removed from the inputfile')
with open(sys.argv[1],'r') as ifile:
      newstring1=re.sub(r'//.*',' ',ifile.read())
with open(sys.argv[1],'w') as ifile:
      ifile.write(newstring1)
print('// have been removed from the inputfile')
0 голосов
/ 23 февраля 2010

Как отмечалось в одном из моих других комментариев, вложение комментариев на самом деле не является проблемой (в C комментарии не вкладываются, хотя несколько компиляторов поддерживают вложенные комментарии в любом случае). Проблема заключается в таких вещах, как строковые литералы, которые могут содержать точно такую ​​же последовательность символов, что и разделитель комментариев, но фактически не равны единице.

Как сказал Майк Грэм, правильный инструмент для работы - это лексер. Парсер не нужен и будет излишним, но лексер - это как раз то, что нужно. Как это случилось, я отправил (частично) лексер для C (и C ++) ранее этим утром. Он не пытается правильно идентифицировать все лексические элементы (то есть все ключевые слова и операторы), но его вполне достаточно для удаления комментариев. Это не принесет никакой пользы на фронте «использования Python», поскольку написано полностью на C (это предшествовало тому, что я использовал C ++ для гораздо большего, чем экспериментальный код).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...