Подтвердить что ты не робот

Удалить комментарии C и C++, используя Python?

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

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

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

4b9b3361

Ответ 1

Я не знаю, знакомы ли вы с sed, основанной на UNIX (но доступной для Windows) программой синтаксического анализа текста, но я нашел sed script здесь, который удалит комментарии 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/С++, и в конечном итоге stripped_code будет содержать код C/С++ с удаленными комментариями. Конечно, если у вас есть файл на диске, вы можете иметь переменные input и output, которые будут файлами, указывающими на эти файлы (input в режиме чтения, output в режиме записи). remccoms3.sed - это файл из приведенной выше ссылки, и он должен быть сохранен в читаемом месте на диске. sed также доступен в Windows и устанавливается по умолчанию на большинстве дистрибутивов GNU/Linux и Mac OS X.

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

Ответ 2

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

Ответ 3

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

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

Для этого требуется флаг "Single line" (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 должно работать очень хорошо, но оно обрабатывает только строки с двойными кавычками.

Ответ 4

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

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

Ответ 5

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

Чтобы описать улучшение несколько более полно: улучшение ведет к сохранению нумерации строк. (Это делается путем сохранения символов новой строки в строках, с помощью которых комментарии 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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

cpp -fpreprocessed foo.c

Ответ 9

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

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

Ответ 10

Следующие работали для меня:

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", который я держу различными инструментами, которые я использую/нуждаюсь.

Ответ 11

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

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

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

Ответ 12

Недавно я столкнулся с этой проблемой, когда я взял класс, где профессор потребовал от нас разбить javadoc из нашего исходного кода, прежде чем отправить его ему для проверки кода. Нам приходилось делать это несколько раз, но мы не могли просто удалить javadoc навсегда, потому что нам также нужно было создавать javadoc html файлы. Вот маленький python script, который я сделал, чтобы сделать трюк. Поскольку javadoc начинается с /** и заканчивается на */, script ищет эти токены, но script может быть изменен для удовлетворения ваших потребностей. Он также обрабатывает однострочные комментарии блоков и случаи, когда конец комментария блока заканчивается, но все еще есть код без комментария в той же строке, что и конец комментария блока. Надеюсь, это поможет!

ПРЕДУПРЕЖДЕНИЕ. Эти скрипты изменяют содержимое переданных файлов и сохраняют их в исходных файлах. Было бы разумно иметь резервную копию где-то еще

#!/usr/bin/python
"""
 A simple script to remove block comments of the form /** */ from files
 Use example: ./strip_comments.py *.java
 Author: holdtotherod
 Created: 3/6/11
"""
import sys
import fileinput

for file in sys.argv[1:]:
    inBlockComment = False
    for line in fileinput.input(file, inplace = 1):
        if "/**" in line:
            inBlockComment = True
        if inBlockComment and "*/" in line:
            inBlockComment = False
            # If the */ isn't last, remove through the */
            if line.find("*/") != len(line) - 3:
                line = line[line.find("*/")+2:]
            else:
                continue
        if inBlockComment:
            continue
        sys.stdout.write(line)