49

Pythonでは、テキストファイルから行を読んだばかりで、行の先頭にハッシュ#が付いたコメントを無視するようにコーディングする方法を知りたいです。

私はそれがこのようなものであるべきだと思います:

for 
   if line !contain #
      then ...process line
   else end for loop 

しかし、私はPythonを初めて使用し、構文がわかりません

4

10 に答える 10

66

あなたはstartswith()を使うことができます

例えば

for line in open("file"):
    li=line.strip()
    if not li.startswith("#"):
        print line.rstrip()
于 2009-11-10T07:38:47.150 に答える
46

#文字が表示されたときに行全体を無視しないことをお勧めします。残りの行は無視してください。と呼ばれる文字列メソッド関数を使用すると、簡単に実行できます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

「初心者」とタグ付けされた質問でこれを複雑にしたくはありませんでしたが、このステートマシンはかなり単純であり、興味深いものになることを願っています.

于 2009-11-10T08:13:19.953 に答える
9

遅くなりましたが、シェル スタイル (または Python スタイル)#のコメントの処理の問題は非常に一般的なものです。

私はテキスト ファイルを読むたびに、いくつかのコードを使用しています。
問題は、引用またはエスケープされたコメントを適切に処理しないことです。しかし、単純なケースでは機能し、簡単です。

for line in whatever:
    line = line.split('#',1)[0].strip()
    if not line:
        continue
    # process line

より堅牢なソリューションは、shlexを使用することです。

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 
于 2014-11-27T21:13:28.513 に答える
8

これは可能な限り短い形式です:

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()メソッドは、先頭の空白をすべて削除した文字列のコピーを返します。

于 2009-11-11T14:40:08.473 に答える
5

私は最近、ジェネレーター関数がこれをうまく処理していることを発見しました。同様の機能を使用して、コメント行や空白行などをスキップしました。

私は自分の関数を次のように定義します

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

これは私のすべてのコードで再利用可能であり、処理/ロギングなどを追加できます。私が必要なこと。

于 2012-06-20T22:36:14.113 に答える
5

これが古いスレッドであることは知っていますが、これは私が独自の目的で使用するジェネレーター関数です。コメントが行のどこに表示されていても削除し、先頭/末尾の空白と空白行も削除します。以下のソーステキスト:

# 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)
于 2015-03-09T20:01:45.190 に答える
3

フィルタリング式のよりコンパクトなバージョンは、次のようになります。

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 回の反復でオンザフライで実行されます。

于 2009-11-10T14:08:59.243 に答える
1

私は使う傾向があります

for line  in lines:
    if '#' not in line:
        #do something

これは行全体を無視しますが、rpartition を含む回答には # の前からの情報を含めることができるため、私の支持があります。

于 2009-11-17T00:27:07.580 に答える
1

インラインとラインの両方で機能するコメントを削除するのは良いことです

def clear_coments(f):
    new_text = ''
    for line in f.readlines():
        if "#" in line: line = line.split("#")[0]

        new_text += line

    return new_text
于 2020-12-21T09:54:00.827 に答える