1

指定したファイルを開きたいこのコードがあり、while ループがあるたびにそれをカウントし、最終的に特定のファイルの while ループの総数を出力します。入力ファイルを辞書に変換し、for ループを作成して、while の後にスペースが続く単語が表示されるたびに、WHILE_ に +1 カウントを追加してから、最後に WHILE_ を出力することにしました。

しかし、これはうまくいかないようで、その理由がわかりません。これを修正する助けがあれば大歓迎です。

これは私が現時点で持っているコードです:

WHILE_ = 0
INPUT_ = input("Enter file or directory: ")


OPEN_ = open(INPUT_)
READLINES_ = OPEN_.readlines()
STRING_ = (str(READLINES_))
STRIP_ = STRING_.strip()
input_str1 = STRIP_.lower()


dic = dict()
for w in input_str1.split():
    if w in dic.keys():
        dic[w] = dic[w]+1
    else:
        dic[w] = 1
DICT_ = (dic)


for LINE_ in DICT_:
    if  ("while\\n',") in LINE_:
        WHILE_ += 1
    elif ('while\\n",') in LINE_:
        WHILE_ += 1
    elif ('while ') in LINE_:
        WHILE_ += 1

print ("while_loops {0:>12}".format((WHILE_)))

これは私が作業していた入力ファイルです:

'''A trivial test of metrics
Author: Angus McGurkinshaw
Date: May 7 2013
'''

def silly_function(blah):
    '''A silly docstring for a silly function'''
    def nested():
        pass
    print('Hello world', blah + 36 * 14)
    tot = 0  # This isn't a for statement
    for i in range(10):
        tot = tot + i
        if_im_done = false  # Nor is this an if
    print(tot)

blah = 3
while blah > 0:
    silly_function(blah)
    blah -= 1
    while True:
        if blah < 1000:
            break

出力は 2 のはずですが、現時点で私のコードは 0 を出力します

4

1 に答える 1

7

これは信じられないほど奇妙なデザインです。readlines文字列のリストを取得するために呼び出し、次にそのリストを呼び出します。これにより、すべてが 1 つの大きな文字列に結合され、各行strの引用符がコンマで結合され、角かっこで囲まれ、結果がスペースで分割されます。reprなぜあなたがそのようなことをするのか、私にはわかりません。

あなたの奇妙な変数名、余分な無駄なコード行DICT_ = (dic)などは、物事をさらに難読化するのに役立ちます.

しかし、なぜそれが機能しないのかを説明することはできます。ばかげたことをすべて行った後、印刷してみてください。含まれているキーはとDICT_だけであることがわかります。どちらも探しているパターンのいずれにも一致しないため、カウントは 0 になります。whilewhile'while

WHILE_また、パターンのインスタンスが複数ある場合でも 1 を追加するだけであるため、カウントの辞書全体が役に立たないことにも注意してください。


これは、文字列を難読化せずに復元してから、誤って復元されたバージョンとの照合を試みると、はるかに簡単になります。直接行うだけです。

私がそれに取り組んでいる間に、コードが読みやすく、よりシンプルになり、ファイルがリークしないように、他のいくつかの問題も修正します。手でハックしようとしていたロジックの完全な実装を次に示します。

import collections

filename = input("Enter file: ")
counts = collections.Counter()
with open(filename) as f:
    for line in f:
        counts.update(line.strip().lower().split())
print('while_loops {0:>12}'.format(counts['while']))

サンプル入力でこれを実行すると、正しく取得されます2。そしてそれをハンドルに拡張することiffor簡単で明白です。


ただし、ロジックには重大な問題があることに注意してください。キーワードのように見えても、コメントまたは文字列の途中にあるものはすべて取得されます。コメントや文字列を取り除く何らかのコードを書かなければ、それを回避する方法はありません。ifこれは、 1 ずつカウントしすぎることを意味します。for削除の明白な方法 (<code>line.partition('#')[0] および同様に引用符の場合) は機能しません。まず、 のように、ifキーワードの前に文字列を配置することは完全に有効です"foo" if x else "bar"。第二に、この方法では複数行の文字列を処理できません。

これらの問題、および同様の問題があるため、ほぼ確実に本物のパーサーが必要になります。Python コードを解析しようとしているだけなら、標準ライブラリastモジュールがこれを行うための明白な方法です。さまざまな言語用のクイック & ダーティ パーサーを作成したい場合は、 を試してくださいpyparsing。これは非常に優れており、優れた例がいくつか付属しています。

簡単な例を次に示します。

import ast

filename = input("Enter file: ")
with open(filename) as f:
    tree = ast.parse(f.read())
while_loops = sum(1 for node in ast.walk(tree) if isinstance(node, ast.While))
print('while_loops {0:>12}'.format(while_loops))

または、より柔軟に:

import ast
import collections

filename = input("Enter file: ")
with open(filename) as f:
    tree = ast.parse(f.read())
counts = collections.Counter(type(node).__name__ for node in ast.walk(tree))    
print('while_loops {0:>12}'.format(counts['While']))
print('for_loops {0:>14}'.format(counts['For']))
print('if_statements {0:>10}'.format(counts['If']))
于 2013-05-29T01:18:39.760 に答える