Удалить C и комментарии C++ с помощью Python?

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

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

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

41
задан jww 26 July 2019 в 03:10
поделиться

9 ответов

Я не знаю, знакомы ли Вы с 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
ответ дан zvoase 27 November 2019 в 00:10
поделиться

можно быть в состоянии усилить py ++ для парсинга источника C++ с GCC.

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

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

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

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

3
ответ дан 3 revs 27 November 2019 в 00:10
поделиться

Не забывайте, что в C, новая строка обратной косой черты устраняется, прежде чем комментарии обрабатываются, и trigraphs обрабатываются перед этим (потому что?? / trigraph для обратной косой черты). У меня есть программа 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 */

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

6
ответ дан Jonathan Leffler 27 November 2019 в 00:10
поделиться

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

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

Это требует флага “Single line” (Re.S), потому что комментарий C может охватить несколько строк.

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

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

РЕДАКТИРОВАНИЕ/: Заметьте, что мой выше кода на самом деле делает предположение об окончаниях строки! Этот код не будет работать над текстовым файлом Mac. Однако это может быть исправлено относительно легко:

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

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

РЕДАКТИРОВАНИЕ/: MizardX и Brian (в комментариях) сделали допустимое замечание об обработке строк. Я полностью забыл обо что, потому что вышеупомянутое regex щипается от модуля парсинга, который имеет дополнительную обработку для строк. Решение MizardX должно работать очень хорошо, но оно только обрабатывает дважды заключенные в кавычки строки.

25
ответ дан Doron Yaacoby 27 November 2019 в 00:10
поделиться

Это обрабатывает комментарии в стиле С++, комментарии 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: литералы символа Added, так как они могли содержать кавычки, которые будут иначе распознаны как строковые разделители.

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

81
ответ дан Scis 27 November 2019 в 00:10
поделиться

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

4
ответ дан Alex Coventry 27 November 2019 в 00:10
поделиться

На самом деле вам не нужно дерево синтаксического анализа, чтобы сделать это идеально, но вам действительно нужен поток токенов, эквивалентный тому, что создается клиентской частью компилятора. Такой поток токенов обязательно должен позаботиться обо всех странностях, таких как начало комментария с продолжением строки, начало комментария в строке, нормализация триграфа и т. Д. Если у вас есть поток токенов, удалить комментарии легко. (У меня есть инструмент, который создает именно такие потоки токенов, как, угадайте, что, внешний интерфейс реального парсера, который создает реальное дерево синтаксического анализа :).

Тот факт, что токены индивидуально распознаются регулярными выражениями, предполагает, что вы, в принципе, можете написать регулярное выражение, которое будет выделять лексемы комментариев. Реальная сложность установленных регулярных выражений для токенизатора (по крайней мере, написанного нами) предполагает, что вы не можете сделать это на практике; писать их по отдельности было достаточно сложно. Если вы не хотите делать это идеально, что ж, тогда большинство из приведенных выше решений RE вполне подойдут.

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

0
ответ дан 27 November 2019 в 00:10
поделиться

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

cpp -fpreprocessed foo.c
1
ответ дан 27 November 2019 в 00:10
поделиться

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

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

1
ответ дан 27 November 2019 в 00:10
поделиться
Другие вопросы по тегам:

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