Фрагмент Python для удаления комментариев C и C ++ - PullRequest
41 голосов
/ 27 октября 2008

Я ищу код Python, который удаляет комментарии C и C ++ из строки. (Предположим, что строка содержит весь исходный файл C).

Я понимаю, что могу подстроку .match () использовать регулярное выражение, но это не решает вложение /* или наличие // внутри /* */.

В идеале я бы предпочел не наивную реализацию, которая правильно обрабатывает неловкие случаи.

Ответы [ 12 ]

80 голосов
/ 28 октября 2008

Обрабатывает комментарии в стиле C ++, комментарии в стиле C, строки и их простое вложение.

def comment_remover(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return " " # note: a space and not an empty string
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

Строки должны быть включены, потому что маркеры комментариев внутри них не запускают комментарий.

Редактировать: re.sub не взял никаких флагов, поэтому сначала пришлось скомпилировать шаблон.

Edit2: Добавлены символьные литералы, поскольку они могут содержать кавычки, которые в противном случае были бы распознаны как разделители строк.

Edit3: Исправлен случай, когда допустимым выражением int/**/x=5; становилось intx=5;, которое не компилировалось, заменяя комментарий пробелом, а не пустой строкой.

25 голосов
/ 27 октября 2008

C (и C ++) комментарии не могут быть вложенными. Регулярные выражения работают хорошо:

//.*?\n|/\*.*?\*/

Для этого требуется флаг «Одна строка» (Re.S), потому что комментарий C может занимать несколько строк.

def stripcomments(text):
    return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S)

Этот код должен работать.

/ EDIT: обратите внимание, что мой код на самом деле делает предположение о конце строки! Этот код не будет работать с текстовым файлом Mac. Тем не менее, это может быть исправлено относительно легко:

//.*?(\r\n?|\n)|/\*.*?\*/

Это регулярное выражение должно работать со всеми текстовыми файлами, независимо от их окончаний (охватывает окончания строк в Windows, Unix и Mac).

/ EDIT: MizardX и Брайан (в комментариях) сделали правильное замечание по поводу обработки строк. Я полностью забыл об этом, потому что приведенное выше регулярное выражение извлечено из модуля синтаксического анализа, который имеет дополнительную обработку для строк. Решение MizardX должно работать очень хорошо, но оно обрабатывает только строки в двойных кавычках.

7 голосов
/ 28 октября 2008

Я не знаю, знакомы ли вы с sed, базирующейся на UNIX (но доступной для Windows) программой синтаксического анализа текста, но я нашел скрипт sed здесь , который удалит C / C ++ комментарии из файла. Это очень умно; например, он будет игнорировать «//» и «/ *», если он найден в объявлении строки, и т. д. В Python его можно использовать, используя следующий код:

import subprocess
from cStringIO import StringIO

input = StringIO(source_code) # source_code is a string with the source code.
output = StringIO()

process = subprocess.Popen(['sed', '/path/to/remccoms3.sed'],
    input=input, output=output)
return_code = process.wait()

stripped_code = output.getvalue()

В этой программе source_code - это переменная, содержащая исходный код C / C ++, и в конечном итоге stripped_code будет содержать код C / C ++ с удаленными комментариями. Конечно, если у вас есть файл на диске, вы можете иметь переменные input и output в качестве файловых дескрипторов, указывающих на эти файлы (input в режиме чтения, output в режиме записи). remccoms3.sed - это файл по вышеуказанной ссылке, и его следует сохранить в удобочитаемом месте на диске. sed также доступен в Windows и устанавливается по умолчанию в большинстве дистрибутивов GNU / Linux и Mac OS X.

Это, вероятно, будет лучше, чем решение на чистом Python; не нужно изобретать велосипед.

6 голосов
/ 14 августа 2013

Эта публикация предоставляет зашифрованную версию улучшения кода Маркуса Жардеро, который был описан atikat в комментарии к публикации Маркуса Жардеро. (Спасибо обоим за предоставленный оригинальный код, который сэкономил мне много работы.)

Чтобы описать улучшение несколько более полно: улучшение сохраняет нетронутой нумерацию строк. (Это достигается сохранением символов новой строки в строках, которыми заменяются комментарии C / C ++.)

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

import re

def removeCCppComment( text ) :

    def blotOutNonNewlines( strIn ) :  # Return a string containing only the newline chars contained in strIn
        return "" + ("\n" * strIn.count('\n'))

    def replacer( match ) :
        s = match.group(0)
        if s.startswith('/'):  # Matched string is //...EOL or /*...*/  ==> Blot out all non-newline chars
            return blotOutNonNewlines(s)
        else:                  # Matched string is '...' or "..."  ==> Keep unchanged
            return s

    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )

    return re.sub(pattern, replacer, text)
6 голосов
/ 28 октября 2008

Не забывайте, что в C обратная косая черта-новая строка удаляется до обработки комментариев, а триграфы обрабатываются до этого (потому что ?? / - это триграф обратной косой черты). У меня есть программа на языке C, называемая SCC (раздельные комментарии C / C ++), и вот часть тестового кода ...

" */ /* SCC has been trained to know about strings /* */ */"!
"\"Double quotes embedded in strings, \\\" too\'!"
"And \
newlines in them"

"And escaped double quotes at the end of a string\""

aa '\\
n' OK
aa "\""
aa "\
\n"

This is followed by C++/C99 comment number 1.
// C++/C99 comment with \
continuation character \
on three source lines (this should not be seen with the -C fla
The C++/C99 comment number 1 has finished.

This is followed by C++/C99 comment number 2.
/\
/\
C++/C99 comment (this should not be seen with the -C flag)
The C++/C99 comment number 2 has finished.

This is followed by regular C comment number 1.
/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++  comment!

This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.

This is followed by regular C comment number 3.
/\
\
\
\
* C comment */

Это не иллюстрирует триграфы. Обратите внимание, что у вас может быть несколько обратных косых черт в конце строки, но сращивание строки не заботится о том, сколько их есть, но возможна последующая обработка. И т.д. Написание одного регулярного выражения для обработки всех этих случаев будет нетривиальным (но это отличается от невозможного).

4 голосов
/ 28 октября 2008

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

3 голосов
/ 27 октября 2008

вы можете использовать py ++ для анализа источника C ++ с помощью GCC.

Py ++ не изобретает велосипед. Это использует компилятор GCC C ++ для разбора C ++ исходные файлы. Чтобы быть более точным, Цепочка инструментов выглядит так:

исходный код передается в GCC-XML GCC-XML передает его компилятору GCC C ++ GCC-XML генерирует описание XML программы на C ++ из внутреннего GCC представление. Py ++ использует pygccxml пакет для чтения сгенерированного GCC-XML файл. Суть - вы можете быть уверен, что все ваши заявления прочитайте правильно.

или, может быть, нет. независимо от того, это не тривиальный анализ.

@ Решения на основе RE - вы вряд ли найдете RE, который правильно обрабатывает все возможные «неловкие» случаи, если вы не ограничите ввод (например, без макросов). для пуленепробиваемого решения у вас действительно нет выбора, кроме как использовать настоящую грамматику.

1 голос
/ 25 сентября 2013

У меня сработало следующее:

from subprocess import check_output

class Util:
  def strip_comments(self,source_code):
    process = check_output(['cpp', '-fpreprocessed', source_code],shell=False)
    return process 

if __name__ == "__main__":
  util = Util()
  print util.strip_comments("somefile.ext")

Это комбинация подпроцесса и препроцессора cpp. Для моего проекта у меня есть служебный класс «Util», в котором хранятся различные инструменты, которые я использую / нуждаюсь.

1 голос
/ 18 августа 2009

Существует также не-Python ответ: используйте программу stripcmt :

StripCmt - простая утилита, написанная в C удалить комментарии из C, C ++, и исходные файлы Java. В большом традиция обработки текста Unix программы, он может функционировать как Фильтр FIFO (First In - First Out) или принимать аргументы в командной строке.

1 голос
/ 03 июля 2009

Извините, это не решение Python, но вы также можете использовать инструмент, который понимает, как удалять комментарии, например ваш препроцессор C / C ++. Вот как GNU CPP делает это .

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