- ベストアンサー
問題文からテキストファイルの一部を抽出する方法
- 問題文にあるテキストファイルから特定のキーワードを使って一部を抽出するプログラムを作成しています。
- プログラムは一部は取得できるものの、何度も繰り返し出力されてしまいます。
- どうすれば修正できるのか、助言をいただきたいです。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
フラグ管理バージョンのプログラムだとこんな感じ。 順次printしてしまうので関数にするのは変かなと思い、メインプログラムに全部書いてしまっています。 ----以下プログラム---- import re path = 'C:\\Users\\xxxxxx\\Desktop\\python\\' file_name = './' + 'data.txt' target = path + file_name block = input() #block = "100" with open(target) as f: ___ find = False # 見つけたフラグ ___ line = f.readline() # 1行ずつ処理 ___ while line: ___ ___ if find: ___ ___ ___ if re.search("[0-9][0-9][0-9]", line): ___ ___ ___ ___ break; # 3ケタ数値を見つけたら脱出 ___ ___ ___ print(line, end='') # そうじゃなきゃlineをprint ___ ___ elif re.search(block, line): #ブロック開始キーワードがあったら ___ ___ ___ find = True; # 見つけた! ___ ___ line = f.readline() # 次の行を読み込む ----ここまで---- 管理するフラグが1つだけならまあ単純でいいのですが、フラグ変数が2つ・3つと増えたりすると、フラグ制御を間違えたり、予想外のフラグ変化に対応できなくて不正な動作をしてしまったりと、なかなか取りにくいバグを生じさせたりする可能性のある、プログラミング法でもあります。
その他の回答 (3)
- asciiz
- ベストアンサー率70% (6825/9705)
readlines() で一気に取り込まず、readline() で一行ずつ見ていく方式で、組みなおしてみました。 あと回答欄の複数スペースは詰まってしまうので、スペース4個を「___ 」(アンダーバー3個+スペース1個)に置換してあります。 コピペ実行する際には「___」を元通り置換してください。 そしてブロックの区切りも必ず「B」で終わるわけではないので、次の「*123*」みたいなもの、すなわち「3ケタ数値」の行が出てきたらその前で終了だ、という判断に変えています。 その結果、extract_text_in_file 関数に与えるパラメータは、ファイルへのパス名と、開始行のキーワードのみとしています。 (本当は、データの方に明確なブロック区切りが欲しいところです。) 実行すると「block = input()」の部分で入力待ちになるので、100とか200とか入れればそのブロックを表示できます。 ----以下プログラム---- import re def extract_text_in_file(filepath, keyword): ___ extracted_text_array = [] ___ ___ with open(filepath) as f: ___ ___ line = f.readline() # 1行ずつ処理 ___ ___ while line: ___ ___ ___ if re.search(keyword, line): #ブロック開始キーワードがあったら ___ ___ ___ ___ line = f.readline() ___ ___ ___ ___ while line: ___ ___ ___ ___ ___ if re.search("[0-9][0-9][0-9]", line): ___ ___ ___ ___ ___ ___ break # 3ケタ数値を見つけたら脱出 ___ ___ ___ ___ ___ else: ___ ___ ___ ___ ___ ___ extracted_text_array.append(line) ___ ___ ___ ___ ___ line = f.readline() ___ ___ ___ ___ break ___ ___ ___ line = f.readline() ___ return extracted_text_array path = 'C:\\Users\\xxxxxx\\Desktop\\python\\' file_name = './' + 'data.txt' target = path + file_name block = input() #block = "100" x = extract_text_in_file(target, block) print(x) ----ここまで---- しかし私もpython不慣れなもんで、なんだかpythonらしくないプログラムになっちゃいました? あ、あとプログラム中のコメントは、上記にあるように、# を置いた後に書きます。 実行時、各行の # 以降は無いものとして扱われます。
お礼
うまく動作しました。 ありがとうございます。 >readlines() で一気に取り込まず、readline() で一行ずつ見ていく方式で、組みなおしてみました。 なるほど、sをなくすと一行づつになるんですね。。
- wormhole
- ベストアンサー率28% (1626/5665)
とりあえず一部分だけですが >lines_strip = "".join(lines_strip) これ、何をしているのか理解していますか? おそらくは https://okwave.jp/qa/q9758394.html の#3の方の回答をそのまま使用されたのでしょうけど、#3の方の回答はあくまでエラーが発生しなくしているだけの例で、あなたがやろうとしている事に適切な対処法なのかどうかは別の話ですよ。
- asciiz
- ベストアンサー率70% (6825/9705)
どう直そうかなと考えているところですが、とりあえずマズいと思うのは >'抽出したい範囲' >pattern = pattern_prev + '(.*)' + pattern_next こちら。 「.*」という正規表現は、その条件にマッチする「最長」部分を取り出します。 例えば、 -111-222-333-444-111-222-333-444-111-222-333-444- という文字列において、「-222.*444-」というパターンは、"-222-333-444-"ではなく、 "-222-333-444-111-222-333-444-111-222-333-444-" にマッチします。 たまたま、'B' がデータ内に1か所しかないため、'100'開始の場合にはうまく動きますが、200・300開始部分を取り出すには、ブロック終端の指定のしようがありません。 「データブロックの区切りを何にするか」を考えて、統一されるようデータの方を改編した方が良いと思います。 どうしても現状のままでやるならば、「先頭が空白でない」ということが目印に出来そうですが…。 ---- 何重にも動作してしまう件は、それぞれの段階で print() していたら、わかったかもしれません。 > lines = f.readlines() > print(lines) #debug 結果: ['<100>\n', '\u3000A xxx\n', '\u3000A ddd\n', '\u3000A ccc\n', '\u3000A vvv\n', '\u3000B zzz\n', '//200//\n', '\u3000A xx1\n', '\u3000A dd1\n', '\u3000A cc1\n', '\u3000A vv1\n', '\u3000A zz1\n', '・300\n', '\u3000A 3xx\n', '\u3000A 3dd\n', '\u3000A 3cc\n', '\u3000A 3vv\n', '\u3000A 3zz\n'] > lines_strip = [line.strip() for line in lines] > lines_strip = "".join(lines_strip) > print(lines_strip) #debug 結果: <100>A xxxA dddA cccA vvvB zzz//200//A xx1A dd1A cc1A vv1A zz1・300A 3xxA 3ddA 3ccA 3vvA 3zz つまりあなたの意図通り改行は削除されましたが、.join で結合してしまったため、リストではなく長い1行の文字列データになってしまったんです。 その一行となった lines_strip で > for i in lines_strip: としてしまうと今度は、lines_stripの長さ分だけ、繰り返すことになります。 たぶんあなたは、lines_strip の中身はリストだと思って、リストの要素数ぶん繰り返そうと思ったのではないかと思うんですけども、pythonインタプリタは、文字列の先頭から1文字ずつ処理して、その長さ回数分ループしてしまったのですね。 ---- うーん、どう直しますかね… 正規表現検索でブロック全体をマッチさせるのはいったん止めて、行単位の処理を考える方が…?
お礼
>「.*」という正規表現は、その条件にマッチする「最長」部分を取り出します。 >たまたま、'B' がデータ内に1か所しかないため、'100'開始の場合にはうまく動きますが、>200・300開始部分を取り出すには、ブロック終端の指定のしようがありません。 なるほど、知りませんでした。 >つまりあなたの意図通り改行は削除されましたが、.join で結合してしまったため、リストでは>なく長い1行の文字列データになってしまったんです。 joinを省いたりしたりしたんですが、エラーが出て解決できませんでした。。
お礼
たくさんの解放をご教示いただきありがとうございます。 もっと、理解を深めて、使えるように頑張ります。