Вопрос

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

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

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

Это было полезно?

Решение

Я не знаю, знакомы ли вы с 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-решение; не нужно изобретать велосипед.

Другие советы

Это обрабатывает комментарии в стиле 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)

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

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

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

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

Комментарии

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

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

Для этого требуется & # 8220; Отдельная строка & # 8221; флаг ( 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 должно работать очень хорошо, но оно обрабатывает только строки в двойных кавычках.

Не забывайте, что в 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 */

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

Эта публикация предоставляет зашифрованную версию усовершенствования кода Маркуса Жардеро, который был описан 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)

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

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

  

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

     

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

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

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

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

cpp -fpreprocessed foo.c

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

  

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

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

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

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

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

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

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

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

#!/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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top