CおよびC ++コメントを削除するPythonスニペット
質問
文字列からCおよびC ++コメントを削除するPythonコードを探しています。 (文字列にCソースファイル全体が含まれていると仮定します。)
正規表現で部分文字列を.match()できることはわかっていますが、 / *
のネストや内の
。 //
の解決はできません> / * * /
理想的には、厄介なケースを適切に処理する素朴な実装を好むでしょう。
解決
UNIXベース(ただしWindowsでも利用可能)のテキスト解析プログラムである sed
に慣れているかどうかはわかりませんが、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)
文字列を含める必要があります。文字列内のコメントマーカーはコメントを開始しないためです。
編集: re.subはフラグを使用しなかったため、最初にパターンをコンパイルする必要がありました。
Edit2:文字リテラルを追加しました。そうしないと文字列の区切り文字として認識される引用符を含めることができるためです。
Edit3:有効な式 int / ** / x = 5;
が intx = 5;
になってしまうケースを修正コンパイルではなく、コメントを空の文字列ではなくスペースに置き換えます。
C(およびC ++)コメントはネストできません。正規表現はうまく機能します:
//.*?\n|/\*.*?\*/
これには、“単一行”が必要です。 Cコメントは複数行にわたることができるため、フラグ( Re.S
)。
def stripcomments(text):
return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S)
このコードは動作するはずです。
/ EDIT:上記のコードは実際に行末について仮定していることに注意してください!このコードは、Macテキストファイルでは機能しません。ただし、これは比較的簡単に修正できます。
//.*?(\r\n?|\n)|/\*.*?\*/
この正規表現は、行末に関係なく、すべてのテキストファイルで機能するはずです(Windows、Unix、およびMacの行末をカバーします)。
/ EDIT:MizardXとBrian(コメント内)は、文字列の処理について有効な発言をしました。上記の正規表現は、文字列の追加処理を行う解析モジュールから引き出されているため、私はそれを完全に忘れていました。 MizardXのソリューションは非常にうまく機能するはずですが、二重引用符で囲まれた文字列のみを処理します。
Cでは、コメントが処理される前にバックスラッシュと改行が削除され、その前にトライグラフが処理されることを忘れないでください(?? /はバックスラッシュのトライグラフであるため)。 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 */
これは、トライグラフを示していません。行の最後に複数のバックスラッシュを含めることができますが、行のスプライシングはその数を気にしませんが、その後の処理では重要になることに注意してください。これらすべてのケースを処理するために単一の正規表現を作成することは簡単ではありません(ただし、それは不可能とは異なります)。
この投稿は、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)
文字列リテラルにコメント構文に一致するサブシーケンスが含まれる場合など、状況によっては正規表現のケースが失敗します。これを処理するには、解析ツリーが本当に必要です。
py ++ を利用してC ++ソースを解析できる場合がありますGCC。
Py ++は車輪を再発明しません。それ GCC C ++コンパイラを使用してC ++を解析します ソースファイル。より正確には、 ツールチェーンは次のようになります。
ソースコードはGCC-XMLに渡されます GCC-XMLはそれをGCC C ++コンパイラに渡します GCC-XMLはXML記述を生成します GCCの内部からのC ++プログラムの 表現。 Py ++はpygccxmlを使用します 生成されたGCC-XMLを読み取るためのパッケージ ファイル。一番下の行-あなたがすることができます 確かに、あなたの宣言はすべて 正しく読みます。
または、そうでないかもしれません。にもかかわらず、これは簡単な解析ではありません。
@ REベースのソリューション-入力を制限しない限り(マクロなしなど)、考えられるすべての「厄介な」ケースを正しく処理するREを見つけることはできません。防弾ソリューションの場合、実際の文法を活用する以外に選択肢はありません。
申し訳ありませんが、これはPythonソリューションではありませんが、C / C ++プリプロセッサなど、コメントを削除する方法を理解するツールを使用することもできます。 GNU CPP それを行う方法。
>cpp -fpreprocessed foo.c
Python以外の回答もあります。プログラム stripcmt を使用してください:
StripCmtは書かれたシンプルなユーティリティです C、C ++からコメントを削除するC およびJavaソースファイル。グランドで Unixテキスト処理の伝統 プログラム、それはどちらかとして機能できます FIFO(先入れ先出し)フィルターまたは コマンドラインで引数を受け入れます。
次は私のために働いた:
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"というユーティリティクラスがあります。使用/必要なさまざまなツールを保持していること。
これを完全に行うために解析ツリーは実際には必要ありませんが、実際には、コンパイラのフロントエンドによって生成されるものと同等のトークンストリームが必要です。このようなトークンストリームは、行継続コメントの開始、文字列のコメントの開始、3文字表記の正規化など、すべての奇妙さを必ず処理する必要があります。トークンストリームがある場合、コメントの削除は簡単です。 (実際の解析ツリーを生成する実際のパーサーのフロントエンドとして、そのようなトークンストリームを正確に生成するツールがあります:)。
トークンが正規表現によって個別に認識されるという事実は、原則として、コメントの語彙素を抽出する正規表現を作成できることを示唆しています。トークナイザーの正規表現セットの実際の複雑さ(少なくとも私たちが書いたもの)は、実際にはこれを実行できないことを示唆しています。それらを個別に書くことは十分に困難でした。完璧にやりたくない場合は、上記のREソリューションのほとんどは問題ありません。
今、あなたがコード難読化ツールを構築しているのでなければ、コメントを削除したいのはなぜです。この場合、完全に正しくする必要があります。
最近この問題に出くわしたのは、教授がコードレビューのためにjavadocを送信する前にソースコードから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)