Используя 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, Вы заканчиваете с двумя проблемами.


Править: Оглядывание назад на это несколько лет спустя. (после того, как немного больше опыта парсинга)

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

25
задан Guillaume Jacquenot 8 June 2017 в 18:53
поделиться

7 ответов

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
29
ответ дан 28 November 2019 в 17:50
поделиться
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
1
ответ дан 28 November 2019 в 17:50
поделиться

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

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

0
ответ дан 28 November 2019 в 17:50
поделиться

Я бы предложил использовать НАСТОЯЩИЙ парсер, например 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);
} 

Он оставляет лишнюю новую строку везде, где удаляются комментарии, но это можно исправить.

16
ответ дан 28 November 2019 в 17:50
поделиться

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

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

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

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

4
ответ дан 28 November 2019 в 17:50
поделиться

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

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

1
ответ дан 28 November 2019 в 17:50
поделиться

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

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

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

1
ответ дан 28 November 2019 в 17:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: