web-dev-qa-db-ja.com

Pythonを使用してCおよびC ++コメントを削除しますか?

文字列からCおよびC++のコメントを削除するPythonコードを探しています(文字列にCソースファイル全体が含まれていると想定してください)。

正規表現を使用して.match()部分文字列を.match()できることはわかっていますが、/*のネスト、または//内に/* */があることは解決しません。

理想的には、厄介なケースを適切に処理する単純ではない実装を好むでしょう。

43
TomZ

UNIXベースの(ただしWindowsで利用可能な)テキスト解析プログラムであるsedに精通しているのかどうかはわかりませんが、sedスクリプト here が見つかりました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変数をそれらのファイルを指すファイルハンドルにすることができます(読み取りモードではinputoutput(書き込みモード)。 remccoms3.sedは上記のリンクからのファイルであり、ディスク上の読み取り可能な場所に保存する必要があります。 sedはWindowsでも使用でき、ほとんどのGNU/LinuxディストリビューションおよびMac OS Xにデフォルトでインストールされます。

これはおそらく、純粋なPythonソリューションよりも優れています。ホイールを再発明する必要はありません。

6
zvoase

これは、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;になり、コンパイルされないケースを修正しました。

81
Markus Jarderot

C(およびC++)コメントはネストできません。正規表現はうまく機能します:

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

これには、「単一行」フラグ(Re.S)Cコメントは複数行にまたがることがあるためです。

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

このコードは動作するはずです。

/編集:上記のコードは実際には行末を想定していることに注意してください!このコードはMacのテキストファイルでは機能しません。ただし、これは比較的簡単に修正できます。

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

この正規表現は、行末に関係なく(Windows、Unix、Macの行末をカバーする)すべてのテキストファイルで機能します。

/ EDIT:MizardXとBrian(コメント内)は、文字列の処理について有効な発言をしました。上記の正規表現は、文字列の処理が追加された解析モジュールから取得されるため、私はそれを完全に忘れていました。 MizardXのソリューションは非常にうまく機能しますが、二重引用符で囲まれた文字列のみを処理します。

25
Konrad Rudolph

この投稿は、Markus Jarderotの投稿へのコメントで、atikatによって記述されたMarkus Jarderotのコードに対する改善のコード化されたバージョンを提供します。 (元のコードを提供してくれたおかげで、多くの作業を節約できました。)

改善をいくらか完全に説明するには:改善により、行番号は変更されません。 (これは、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
Menno Rubingh

Cでは、コメントが処理される前にバックスラッシュ改行が削除され、その前に3文字表記が処理されることを忘れないでください(?? /がバックスラッシュの3文字表記であるため)。 SCC(ストリップC/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 */

これはトリグラフを示していません。行の終わりに複数のバックスラッシュを含めることができますが、行のスプライシングではいくつあるかは問題ではありませんが、後続の処理では可能であることに注意してください。これらすべてのケースを処理するために単一の正規表現を書くのは簡単ではありません(しかし、それは不可能とは異なります)。

6

正規表現の場合は、文字列リテラルにコメント構文と一致するサブシーケンスが含まれている場合など、状況によっては失敗します。これに対処するには、本当に解析ツリーが必要です。

4
Alex Coventry

py ++ を利用してGCCでC++ソースを解析できる場合があります。

Py ++はホイールを再発明しません。 GCC C++コンパイラを使用してC++ソースファイルを解析します。より正確には、ツールチェーンは次のようになります。

ソースコードはGCC-XMLに渡されますGCC-XMLはそれをGCC C++コンパイラに渡しますGCC-XMLはGCCの内部表現からC++プログラムのXML記述を生成します。 Py ++はpygccxmlパッケージを使用して、GCC-XMLで生成されたファイルを読み取ります。結論-すべての宣言が正しく読み取られていることを確認できます。

またはそうでないかもしれません。とにかく、これは簡単な解析ではありません。

@ REベースのソリューション-入力を制限しない限り(マクロなしなど)、すべての可能な「ぎこちない」ケースを正しく処理するREを見つけることはほとんどありません。防弾ソリューションの場合、実際の文法を活用する以外に選択肢はありません。

3
Dustin Getz

Python以外の回答もあります。プログラム stripcmt を使用してください。

StripCmtは、C、C++、およびJavaソースファイルからコメントを削除するためにCで記述されたシンプルなユーティリティです。Unixテキスト処理プログラムの偉大な伝統では、FIFO(先入れ先出し))コマンドラインで引数をフィルターまたは受け入れます。

1
hlovdal

以下は私のために働きました:

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

Pythonソリューションではなく申し訳ありませんが、C/C++プリプロセッサなど、コメントを削除する方法を理解するツールを使用することもできます。 GNU CPP それを行う の方法を次に示します。

cpp -fpreprocessed foo.c
1
sigjuice

これを完全に行うために解析ツリーは実際には必要ありませんが、コンパイラのフロントエンドによって生成されるものと同等のトークンストリームが実際に必要です。このようなトークンストリームは、必ず行継続コメントの開始、文字列のコメント開始、3文字表記の正規化など、すべての奇妙なことに対処する必要があります。トークンストリームがある場合、コメントの削除は簡単です。 (私は、実際にパースツリーを生成する実際のパーサーのフロントエンドとして、まさにそのようなトークンストリームを生成するツールを持っています:)。

トークンが正規表現によって個別に認識されるという事実は、原則として、コメントの語彙素を抽出する正規表現を記述できることを示唆しています。トークナイザー(少なくとも私たちが書いたもの)に設定された正規表現の実際の複雑さは、実際にはこれを実行できないことを示唆しています。個別に書くのは大変でした。完璧にしたくない場合は、上記のREソリューションのほとんどで問題ありません。

さて、コードの難読化プログラムを作成していない限り、whyは、コメントを削除することは私を超えているでしょう。この場合、完全に正しくなければなりません。

0
Ira Baxter