Pergunta

Eu estou procurando código Python que remove comentários em C e C ++ a partir de uma string. (Suponha que a string contém um arquivo de origem C inteira.)

Eu percebo que eu poderia .match () substrings com uma Regex, mas que não resolve /* nidificação, ou ter um // dentro de um /* */.

Idealmente, eu preferiria uma implementação não ingênuo que trata adequadamente os casos difíceis.

Foi útil?

Solução

Eu não sei programa de análise de texto se você estiver familiarizado com sed, o baseado em UNIX (mas o Windows disponível no mercado), mas eu encontrei um script sed aqui que irá remover C / C ++ comentários de um arquivo. É muito inteligente; por exemplo, ele irá ignorar '//' e '/ *' se for encontrado em uma declaração de corda, etc. A partir do Python, ele pode ser usado usando o seguinte código:

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()

Neste programa, source_code é a variável que contém o código-fonte C / C ++, e, eventualmente, stripped_code realizará C / C ++ código com os comentários removidos. Claro, se você tem o arquivo no disco, você pode ter as variáveis ??input e output ser identificadores de arquivo apontando para esses arquivos (input em modo de leitura, output no modo de gravação). remccoms3.sed é o arquivo a partir do link acima, e ele deve ser guardado em um local legíveis no disco. sed também está disponível no Windows, e vem instalado por padrão na maioria das distribuições GNU / Linux e Mac OS X.

Esta será provavelmente melhor do que uma solução Python puro; não há necessidade de reinventar a roda.

Outras dicas

Este alças C ++ -. Comentários de estilo, comentários de estilo C, cordas e nidificação simples do mesmo

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)

Cordas precisa ser incluído, por causa de comentário marcadores dentro deles não começar um comentário.

Editar:. re.sub não tomar quaisquer bandeiras, tiveram de compilar o primeiro padrão

Edit2:. literais de caracteres acrescentado, dado que poderia conter citações que seriam interpretados como delimitadores de strings

Edit3:. Corrigido o caso em que um int/**/x=5; expressão legal se tornaria intx=5; que não compilar, substituindo o comentário com um espaço em vez de uma string vazia

C (e C ++) os comentários podem não ser aninhados. As expressões regulares funcionam bem:

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

Isto requer a “linha única” bandeira (Re.S) porque um comentário C pode abranger várias linhas.

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

Este código deve funcionar.

/ EDIT: Note que o meu código acima, na verdade, faz uma suposição sobre terminações de linha! Este código não funcionará em um arquivo de texto Mac. No entanto, isso pode ser alterado de forma relativamente fácil:

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

Esta expressão regular deve funcionar em todos os arquivos de texto, independentemente de suas terminações de linha (tampas Windows, Unix e Mac final de linha).

/ EDIT: MizardX e Brian (nos comentários) fez uma observação válida sobre a manipulação de strings. Eu esqueci completamente sobre isso porque a regex acima é arrancado de um módulo de análise que tem a manipulação adicional para strings. A solução da MizardX deve funcionar muito bem, mas ele só lida com cordas duplas citado.

Não se esqueça que em C, barra invertida-nova linha é eliminado antes dos comentários são processados, e trigraphs são processadas antes que (porque ?? / é o trigraph de barra invertida). Eu tenho um programa C denominado SCC (faixa de C / C ++ comentários), e aqui está parte do código de teste ...

" */ /* 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 */

Esta não ilustra trigraphs. Note que você pode ter várias barras invertidas no fim de uma linha, mas o splicing linha não se importa com quantos são, mas a subseqüente poder de processamento. Etc. escrever uma única regex para lidar com todos esses casos será não-trivial (mas isso é diferente de impossível).

Esta postagem fornece uma versão codificado-out da melhoria ao código de Markus Jarderot que foi descrito por atikat, num comentário à postagem de Markus Jarderot. (Graças a ambos para fornecer o código original, o que me salvou um monte de trabalho.)

Para descrever a melhoria um pouco mais detalhadamente: A melhoria mantém a linha numeração intacta. (Isto é feito, mantendo os caracteres de nova linha intacta nas cordas pelo qual os comentários C / C ++ são substituídos.)

Esta versão da função comentário remoção do C / C ++ é adequado quando você quer gerar mensagens de erro para os usuários (por exemplo, erros de análise) que contêm números de linha (ou seja, números de linha válidos para o texto original).

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)

Os casos de expressão regular vai cair em algumas situações, como onde um literal cadeia contém uma subsequência que corresponde a sintaxe de comentário. Você realmente precisa de uma árvore de análise para lidar com isso.

Você pode ser capaz de alavancagem py ++ o C fonte para analisar ++ com GCC.

Py ++ não reinventar a roda. isto utiliza compilador GCC C ++ para análise C ++ Arquivos Fonte. Para ser mais preciso, o olhares cadeia ferramenta como esta:

código fonte é passado para GCC-XML GCC-XML passa para o compilador GCC C ++ GCC-XML gera uma descrição XML de um programa C ++ do GCC do interno representação. Py ++ usa pygccxml pacote para ler GCC-XML gerado Arquivo. A linha inferior - você pode ser certeza, que todas as suas declarações são ler corretamente.

ou, talvez não. Independentemente disso, este não é um parse trivial.

soluções @ RE base - é improvável que você encontrar um RE que lida com todos os possíveis 'embaraçosas' casos corretamente, a menos que você entrada constrangimento (por exemplo, há macros). para uma solução à prova de balas, você realmente não tem escolha do que alavancar a gramática real.

Sinto que isso não é uma solução Python, mas você também pode usar uma ferramenta que entende como remover comentários, gostar de seu C / C ++ pré-processador. Veja como GNU CPP faz .

cpp -fpreprocessed foo.c

Há também uma resposta não-python: usar o programa stripcmt :

StripCmt é um utilitário simples escrito em C para remover comentários de C, C ++, e arquivos de origem Java. No grande tradição de processamento de texto Unix programas, ele pode funcionar como um FIFO (First In - First Out) filtro ou aceitam argumentos na linha de comando.

A seguir trabalhou para mim:

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")

Esta é uma combinação do subprocesso e o pré-processador cpp. Para o meu projeto eu tenho uma classe utilitário chamado "Util" que eu manter várias ferramentas que eu uso / necessidade.

Você não precisa realmente uma árvore de análise para fazer isso perfeitamente, mas você faz na necessidade efeito do fluxo equivalente token para o que é produzido pelo front end do compilador. Tal fluxo de token deve necessarilyy cuidar de toda a estranheza como comentário start linha contínua, comentário start em string, normalização trigraph, etc. Se você tem o fluxo de forma, eliminar os comentários é fácil. (Eu tenho uma ferramenta que produz exatamente esses fluxos simbólicos, como, adivinhem, a extremidade dianteira de um analisador real que produz uma árvore de análise reais :).

O fato de que os tokens são reconhecidos individualmente por expressões regulares sugere que você pode, em princípio, escrever uma expressão regular que irá escolher o comentário lexemes. A verdadeira complexidade das expressões regulares do jogo para o tokenizer (pelo menos aquele que escreveu) sugere que você não pode fazer isso na prática; escrevê-las individualmente era forte o suficiente. Se você não quiser fazê-lo perfeitamente, bem, então, a maioria das soluções RE acima são muito bem.

Agora, por você gostaria comentários tira está além de mim, a menos que você está construindo um obfuscator código. Neste caso, você tem que tê-lo perfeitamente certo.

deparei com este problema recentemente, quando eu fiz uma aula onde o professor nos obrigou a retirar javadoc do nosso código fonte antes de submetê-lo a ele por uma revisão de código. Tivemos que fazer isso várias vezes, mas não podia simplesmente remover o javadoc permanentemente porque fomos obrigados a gerar arquivos HTML Javadoc também. Aqui está um script python pouco que fiz para fazer o truque. Desde javadoc começa com / ** e termina com * /, os olhares de script para essas fichas, mas o script pode ser modificado de acordo com suas necessidades. Ele também lida com blocos de comentários de linha única e casos em que um bloco de comentários termina, mas ainda há código não comentou sobre a mesma linha que o bloco de comentário final. Espero que isso ajude!

AVISO: Este script modifica o conteúdo dos arquivos passados ??em e salva-los para os arquivos originais. Seria sábio para ter um lugar de backup outra

#!/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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top