Pythonでは、テキストファイルから行を読んだばかりで、行の先頭にハッシュ#が付いたコメントを無視するようにコーディングする方法を知りたいです。
私はそれがこのようなものであるべきだと思います:
for
if line !contain #
then ...process line
else end for loop
しかし、私はPythonを初めて使用し、構文がわかりません
例えば
for line in open("file"):
li=line.strip()
if not li.startswith("#"):
print line.rstrip()
#
文字が表示されたときに行全体を無視しないことをお勧めします。残りの行は無視してください。と呼ばれる文字列メソッド関数を使用すると、簡単に実行できますpartition
。
with open("filename") as f:
for line in f:
line = line.partition('#')[0]
line = line.rstrip()
# ... do something with line ...
partition
タプルを返します: パーティション文字列の前のすべて、パーティション文字列、およびパーティション文字列の後のすべて。したがって、 でインデックスを作成することにより[0]
、パーティション文字列の前の部分だけを取得します。
編集: を持たないバージョンの Python を使用している場合はpartition()
、次のコードを使用できます。
with open("filename") as f:
for line in f:
line = line.split('#', 1)[0]
line = line.rstrip()
# ... do something with line ...
これにより、文字列が「#」文字で分割され、分割前のすべてが保持されます。1
引数は、1 つの.split()
分割後にメソッドを停止させます。0 番目の部分文字列を ( でインデックス付けすることによって[0]
) 取得しているだけなので、引数なしで同じ答えが得られますが1
、これは少し速いかもしれません。(@gnr からのコメントのおかげで、元のコードから単純化されました。元のコードは、正当な理由もなく面倒でした。@gnr に感謝します。)
の独自のバージョンを作成することもできますpartition()
。と呼ばれるものは次のpart()
とおりです。
def part(s, s_part):
i0 = s.find(s_part)
i1 = i0 + len(s_part)
return (s[:i0], s[i0:i1], s[i1:])
@dalle は、「#」が文字列内に表示される可能性があることに注意しました。このケースを正しく処理するのはそれほど簡単ではないので、無視しましたが、何か言うべきでした。
入力ファイルに引用符で囲まれた文字列に関する単純なルールがある場合、これは難しくありません。一重引用符、二重引用符、行末をエスケープするバックスラッシュ付きの複数行の引用符、三重引用符 (一重引用符または二重引用符のいずれかを使用)、および生の弦も!すべてを正しく処理する唯一の方法は、複雑なステート マシンになります。
しかし、引用符で囲まれた単純な文字列だけに限定すれば、単純なステート マシンで処理できます。文字列内でバックスラッシュで引用された二重引用符を使用することもできます。
c_backslash = '\\'
c_dquote = '"'
c_comment = '#'
def chop_comment(line):
# a little state machine with two state varaibles:
in_quote = False # whether we are in a quoted string right now
backslash_escape = False # true if we just saw a backslash
for i, ch in enumerate(line):
if not in_quote and ch == c_comment:
# not in a quote, saw a '#', it's a comment. Chop it and return!
return line[:i]
elif backslash_escape:
# we must have just seen a backslash; reset that flag and continue
backslash_escape = False
elif in_quote and ch == c_backslash:
# we are in a quote and we see a backslash; escape next char
backslash_escape = True
elif ch == c_dquote:
in_quote = not in_quote
return line
「初心者」とタグ付けされた質問でこれを複雑にしたくはありませんでしたが、このステートマシンはかなり単純であり、興味深いものになることを願っています.
遅くなりましたが、シェル スタイル (または Python スタイル)#
のコメントの処理の問題は非常に一般的なものです。
私はテキスト ファイルを読むたびに、いくつかのコードを使用しています。
問題は、引用またはエスケープされたコメントを適切に処理しないことです。しかし、単純なケースでは機能し、簡単です。
for line in whatever:
line = line.split('#',1)[0].strip()
if not line:
continue
# process line
import shlex
for line in instream:
lex = shlex.shlex(line)
lex.whitespace = '' # if you want to strip newlines, use '\n'
line = ''.join(list(lex))
if not line:
continue
# process decommented line
このシュレックス アプローチは、引用符とエスケープを適切に処理するだけでなく、多くの優れた機能を追加します (必要に応じて、他のファイルのソース ファイルを作成する機能など)。大きなファイルの速度についてはテストしていませんが、小さなものについては十分に高速です。
import shlex
for line in instream:
fields = shlex.split(line, comments=True)
if not fields:
continue
# process list of fields
これは可能な限り短い形式です:
for line in open(filename):
if line.startswith('#'):
continue
# PROCESS LINE HERE
文字列のstartswith()
メソッドは、呼び出した文字列が渡した文字列で始まる場合に True を返します。
これは、シェル スクリプトなどの一部の状況では問題ありませんが、2 つの問題があります。まず、ファイルを開く方法が指定されていません。ファイルを開くためのデフォルトのモードは です。これは'r'
、「バイナリ モードでファイルを読み取る」ことを意味します。テキストファイルを期待しているので、で開くことをお勧めします'rt'
。この区別は UNIX ライクなオペレーティング システムでは意味がありませんが、Windows (および OS X 以前の Mac) では重要です。
2 番目の問題は、開いているファイル ハンドルです。このopen()
関数はファイル オブジェクトを返します。ファイルの処理が終わったら、ファイルを閉じることをお勧めします。close()
これを行うには、オブジェクトのメソッドを呼び出します。最終的には、おそらくPython がこれを行います。Python オブジェクトは参照カウントされ、オブジェクトの参照カウントがゼロになると解放され、オブジェクトが解放された後のある時点で、Python はそのデストラクタを呼び出します (特別なメソッドである と呼ばれます)。私がおそらく言ったことに注意してください: Pythonには、プログラムが終了する直前に参照カウントがゼロになるオブジェクトに対して実際にデストラクタを呼び出さないという悪い習慣があります。急いでいると思います!__del__
シェル スクリプトのような短期間のプログラム、特にファイル オブジェクトの場合、これは問題になりません。オペレーティング システムは、プログラムの終了時に開いたままのファイル ハンドルを自動的にクリーンアップします。しかし、ファイルを開いて内容を読み取り、最初にファイル ハンドルを明示的に閉じずに長い計算を開始した場合、Python は計算中にファイル ハンドルを開いたままにする可能性があります。そして、それは悪い習慣です。
このバージョンは Python の任意の 2.x バージョンで動作し、上記で説明した両方の問題を修正します。
f = open(file, 'rt')
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
f.close()
これは、古いバージョンの Python に最適な一般的な形式です。
steveha が示唆したように、「with」ステートメントを使用することがベスト プラクティスと見なされるようになりました。2.6 以降を使用している場合は、次のように記述します。
with open(filename, 'rt') as f:
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
"with" ステートメントは、ファイル ハンドルをクリーンアップします。
質問で「# で始まる行」とおっしゃっていたので、ここでお見せしました。オプションの空白と「#」で始まる行を除外する場合は、「#」を探す前に空白を削除する必要があります。その場合、これを変更する必要があります。
if line.startswith('#'):
これに:
if line.lstrip().startswith('#'):
Python では、文字列は不変であるため、これは の値を変更しませんline
。このlstrip()
メソッドは、先頭の空白をすべて削除した文字列のコピーを返します。
私は最近、ジェネレーター関数がこれをうまく処理していることを発見しました。同様の機能を使用して、コメント行や空白行などをスキップしました。
私は自分の関数を次のように定義します
def skip_comments(file):
for line in file:
if not line.strip().startswith('#'):
yield line
そうすれば、私はただできる
f = open('testfile')
for line in skip_comments(f):
print line
これは私のすべてのコードで再利用可能であり、処理/ロギングなどを追加できます。私が必要なこと。
これが古いスレッドであることは知っていますが、これは私が独自の目的で使用するジェネレーター関数です。コメントが行のどこに表示されていても削除し、先頭/末尾の空白と空白行も削除します。以下のソーステキスト:
# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!
が得られます:
host02
host03
host04
デモを含む文書化されたコードは次のとおりです。
def strip_comments(item, *, token='#'):
"""Generator. Strips comments and whitespace from input lines.
This generator strips comments, leading/trailing whitespace, and
blank lines from its input.
Arguments:
item (obj): Object to strip comments from.
token (str, optional): Comment delimiter. Defaults to ``#``.
Yields:
str: Next uncommented non-blank line from ``item`` with
comments and leading/trailing whitespace stripped.
"""
for line in item:
s = line.split(token, 1)[0].strip()
if s:
yield s
if __name__ == '__main__':
HOSTS = """# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!""".split('\n')
hosts = strip_comments(HOSTS)
print('\n'.join(h for h in hosts))
通常の使用例は、ファイル (つまり、上記の例のようにホスト ファイル) からコメントを削除することです。この場合、上記のコードの末尾を次のように変更します。
if __name__ == '__main__':
with open('aa.txt', 'r') as f:
hosts = strip_comments(f)
for host in hosts:
print('\'%s\'' % host)
フィルタリング式のよりコンパクトなバージョンは、次のようになります。
for line in (l for l in open(filename) if not l.startswith('#')):
# do something with line
(l for ... )
これは「ジェネレーター式」と呼ばれ、ここでは反復処理中にファイルから不要な行をすべて除外するラッピング イテレーターとして機能します。[l for ... ]
最初にファイルからすべての行をメモリに読み込み、それから反復処理を開始する「リスト内包表記」である正方形のブレーキと同じものと混同しないでください。
場合によっては、1 行にまとめずに読みやすくしたい場合があります。
lines = open(filename)
lines = (l for l in lines if ... )
# more filters and mappings you might want
for line in lines:
# do something with line
すべてのフィルターは、1 回の反復でオンザフライで実行されます。
私は使う傾向があります
for line in lines:
if '#' not in line:
#do something
これは行全体を無視しますが、rpartition を含む回答には # の前からの情報を含めることができるため、私の支持があります。
インラインとラインの両方で機能するコメントを削除するのは良いことです
def clear_coments(f):
new_text = ''
for line in f.readlines():
if "#" in line: line = line.split("#")[0]
new_text += line
return new_text