Использование regex для удаления комментариев из исходных файлов
Я создаю программу для автоматизации написания некоторого кода на C (я пишу для синтаксического анализа строк в одноименные перечисления)
C обработка строк не так уж велика.
Поэтому некоторые люди навязывают мне попробовать python.
Я сделал функцию, которая должна удалить C-стиль /* 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:
Оглядываясь назад на эти годы позже. (после справедливого более сильного анализа)
Я думаю, что регулярное выражение, возможно, было правильным решением.
И простое регулярное выражение используется здесь "достаточно хорошо".
Возможно, я не подчеркнул этого в вопросе.
Это было для одного конкретного файла. У этого не было сложных ситуаций.
Я думаю, что было бы намного меньше обслуживания, чтобы файл анализировался достаточно просто для регулярного выражения, чем для усложнения регулярного выражения в нечитаемый суп символа.
Ответы
Ответ 1
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
Ответ 2
Многие ответы уже даны, но;
как насчет "//comment-like strings inside quotes"
?
OP спрашивает, как это сделать, используя регулярные выражения; так:
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)
Удалить WILL:
-
/* 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.
Ответ 3
Я бы предложил использовать парсер REAL, например 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
Я бы порекомендовал вам прочитать эту страницу, которая содержит довольно подробный анализ проблемы и дает хорошее представление о том, почему ваш подход не работает: http://ostermiller.org/findcomment.html
Короткая версия: Регулярное выражение, которое вы ищете, следующее:
(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)
Это должно соответствовать обоим типам блоков комментариев. Если у вас возникли проблемы, прочитайте связанную с ним страницу.
Ответ 5
Вы делаете это неправильно.
Regex для Regular Languages , который C не является.
Ответ 6
Я вижу несколько вещей, которые вы, возможно, захотите пересмотреть.
Во-первых, Python передает объекты по значению, но некоторые типы объектов неизменяемы. Строки и целые числа относятся к этим неизменяемым типам. Поэтому, если вы передаете строку функции, любые изменения в строке, которые вы вносите в функцию, не будут влиять на строку, в которую вы передали. Вместо этого вы должны попробовать вернуть строку. Кроме того, в функции removeComments() вам нужно присвоить значение, возвращенное re.sub(), новой переменной - подобно любой функции, которая принимает строку в качестве аргумента, re.sub() не будет изменять строку.
Во-вторых, я бы сказал, что другие говорили о разборе кода C. Регулярные выражения - не лучший способ пойти сюда.
Ответ 7
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
Ответ 8
Как отмечено в одном из моих других комментариев, вложение комментариев на самом деле не проблема (в C комментарии не гнездятся, хотя несколько компиляторов поддерживают вложенные комментарии в любом случае). Проблема связана с такими вещами, как строковые литералы, которые могут содержать точно такую же последовательность символов, что и разделитель комментариев, фактически не являясь одним из них.
Как сказал Майк Грэм, правильным инструментом для работы является лексер. Парсер не нужен и будет излишним, но лексер - это именно то, что нужно. Как это происходит, я опубликовал (частичный) lexer для C (и С++) ранее утром. Он не пытается правильно идентифицировать все лексические элементы (т.е. Все ключевые слова и операторы), но это вполне достаточно для снятия комментариев. Он не будет делать ничего хорошего на "использовании Python", хотя, поскольку он полностью написан на C (он предшествует моему использованию С++ для гораздо большего, чем экспериментальный код).
Ответ 9
Эта программа удаляет комментарии с//и/* */из заданного файла:
#! /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')
Ответ 10
Просто хочу добавить еще одно регулярное выражение, где мы должны удалить что-нибудь между * и; в питоне
data = re.sub(re.compile("*. *? \;", re.DOTALL), '', data)
перед * стоит косая черта, чтобы избежать метасимвола.