2010-02-23 2 views
21

Я создаю программу для автоматизации написания некоторого кода на C (я пишу для синтаксического анализа строк в одноименных перечислениях) Обработка C строк не так велика , Так что некоторые люди придирались ко мне, чтобы попробовать python.Использование regex для удаления комментариев из исходных файлов

Я сделал функцию, которая должна удалить 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: Оглядываясь на эти годы спустя. (после того, как я получил еще немного опыта синтаксического анализа)

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

+4

Существует только один разумный ответ: http://kore-nordmann.de/blog/do_NOT_parse_using_regexp.html. Он говорил о другом языке, но его заключение остается в силе. –

+0

@Jerry - Строго говоря, вы часто можете угадать разумный предел гнездования и определить правильное приближение языка. В любом случае у многих компиляторов есть ограничение на размещение комментариев. Но - какой предел безопасен? Кроме того, я не хочу отлаживать регулярное выражение. Хорошая связь в любом случае. – Steve314

+1

@ Steve314: вы можете догадаться о разумном пределе вложенности (например, в C, комментарии просто вообще не гнездятся), но это мало помогает. Только для очевидного примера разделитель комментариев в строковом литерале не учитывается, но разделитель комментариев, разбитый по строкам (с обратной косой чертой между символами) * делает * подсчет. Принимая во внимание правильно в RE, в лучшем случае нетривиально. –

ответ

20

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

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

'char * note =" Вы можете сделать однострочный комментарий с // ";' Oops. –

+0

Действительно. Это только ответы, почему функция OP ничего не возвращает. – msanders

+0

Это технически правильный ответ на вопрос. Возможно, использование phasor - лучший способ решить мою проблему, , но это заставило мой код работать. –

2

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

Регулярное выражение для Regular Languages, которое не является.

+0

Конечно, одна из общих ожидаемых различий между лексером и парсером заключается в том, что только лексер поддерживает обычный язык. Не всегда верно (например, Ragel), как с регулярным выражением. Хороший лексер может выполнить эту работу, но, как и при использовании парсера, это похоже на массовый перебор только для комментариев. – Steve314

+0

@ Steve314. Если «overkill» вы имеете в виду * полностью правильный инструмент для работы *, то да. Все регулярные выражения, размещенные здесь, чрезвычайно ошибочны и не будут делать правильные вещи, когда сталкиваются с действительным реалистичным кодом C (++). –

+0

Читайте о lexer, удалили мою рекомендацию lexer –

15

Я бы предложил использовать синтаксический анализатор REAL, например SimpleParse или PyParsing. SimpleParse требует, чтобы вы действительно знали EBNF, но очень быстро. PyParsing имеет свой собственный синтаксис, подобный EBNF, но который адаптирован для Python и делает его легким для создания мощных точных парсеров.

Edit:

Вот пример того, как легко использовать 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); 
} 

Это оставляет дополнительный символ новой строки, где он раздетый комментарии, но которые могут быть решены.

+0

Regex - это плохо, но синтаксический анализ является излишним? Я запутался; что еще там? – jathanism

+0

Я искал проблему неправильно - поиск на основе простого альтернативного регулярного выражения намного проще, чем писать парсер. Тем не менее, это не касается путаницы, вызванной вещами в строках. Парсер (или Лексер), как прокомментировал Майк, может быть правильным инструментом для работы. – Steve314

+0

Да, Regex «легко», если ваш вход прост, например, в том, что касается согласованного формата, такого как IP-адреса или номера телефонов. Для всех остальных: lexer. – jathanism

4

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

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

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

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

+0

Если я что-то пропустил, это пропустит разделитель комментариев, сращенный по строкам (слэш, обратная косая черта, новая строка, звездочка или звездочка, обратная косая черта, новая линия, косая черта). Хуже того, что обратная косая черта может быть сгенерирована как последовательность триграмм '?? /' (хотя я соглашусь, что триграфы довольно редки). –

1

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

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

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

0

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

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

1
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 
0

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

#! /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

Вы должны объяснить, как вы решаете проблему PO! – hivert

+0

Извините, не понял, что такое ПО? – harishli2020

+0

Жаль, что я имел в виду OP (http://meta.stackexchange.com/questions/79804/whats-stackexchange-ese-for-op) – hivert

26

Многие ответы даны уже, но;
Что относительно "//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 источника.

+0

Что насчет '/ * Возможно, мы //" не должны/* делать это */// но давайте сделаем это */// "anyway' (вставляем строки по желанию) –

+0

Уже рассмотрен в текущем коде .. Вы можете попробовать. –

+0

Умный, он даже управляет чередованием à la ''a =/* a" -nested string */"комментарии могут заканчиваться на * /" // comment2''! Я вижу, что группировка делает regexes намного более мощным и понятным. Интересно, все же можно было бы построить действительный C-код, который заставит ваше регулярное выражение сработать;) Но это, вероятно, будет связано с некоторыми очень неприятными материалами прекомпилятора ... –