Я делаю программу для автоматизации записи некоторого кода 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, Вы заканчиваете с двумя проблемами.
Править: Оглядывание назад на это несколько лет спустя. (после того, как немного больше опыта парсинга)
Я думаю, что regex, возможно, был правильным решением. И простой regex, используемый здесь "достаточно хороший". Я не мог подчеркнуть это достаточно в вопросе. Это было для единственного определенного файла. Это не имело никаких щекотливых ситуаций. Я думаю, что это было бы намного меньше обслуживания для хранения файла проанализированным достаточно простой для regex, чем усложнить regex, в суп нечитаемого символа.
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
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("//")]
output
$ ./python.py
blah1
blah2
blah3
Как отмечалось в одном из моих других комментариев, вложение комментариев на самом деле не проблема (в C комментарии не вкладываются, хотя некоторые компиляторы в любом случае поддерживать вложенные комментарии). Проблема заключается в таких вещах, как строковые литералы, которые могут содержать ту же последовательность символов, что и разделитель комментариев, но на самом деле не являются таковыми.
Как сказал Майк Грэм, верный инструмент для работы - лексер. Парсер не нужен и будет излишним, но лексер - это то, что нужно. Так получилось, что сегодня утром я опубликовал (частичный) лексер для C (и C ++). Он не пытается правильно идентифицировать все лексические элементы (т.е. все ключевые слова и операторы), но этого вполне достаточно для удаления комментариев. Это не принесет никакой пользы в плане «использования Python», поскольку он полностью написан на C (он предшествовал моему использованию C ++ для гораздо большего, чем экспериментальный код).
Я бы предложил использовать НАСТОЯЩИЙ парсер, например 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);
}
Он оставляет лишнюю новую строку везде, где удаляются комментарии, но это можно исправить.
Я бы порекомендовал вам прочитать эту страницу, которая содержит довольно подробный анализ проблемы и дает хорошее понимание того, почему ваша подход не работает: http://ostermiller.org/findcomment.html
Краткая версия: регулярное выражение, которое вы ищете, следующее:
(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)
Оно должно соответствовать обоим типам блоков комментариев. Если у вас возникли проблемы после этого, прочтите страницу, на которую я ссылаюсь.
Вы делаете это неправильно.
Регулярное выражение предназначено для обычных языков , а C - нет.
Я вижу несколько вещей, которые вы, возможно, захотите исправить.
Во-первых, Python передает объекты по значению, но некоторые типы объектов неизменяемы. Строки и целые числа относятся к этим неизменяемым типам. Поэтому, если вы передадите строку в функцию, любые изменения строки, которые вы сделаете внутри функции, не повлияют на строку, которую вы передали. Вместо этого вы должны попробовать вернуть строку. Кроме того, в функции removeComments () вам необходимо присвоить значение, возвращаемое re.sub (), новой переменной - как и любая функция, которая принимает строку в качестве аргумента, re.sub () не будет изменять строку.
Во-вторых, я хотел бы повторить то, что другие говорили о синтаксическом анализе кода C. Регулярные выражения - не лучший вариант.