質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.90%

Python 範囲指定及び値の取り出し

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 219

polpop

score 6

実現したい事

sample.txtから条件に合う特定の値を出力したいです。
(結果1~10までの内容が記入されています。)
条件は
『SQL order by Elapsed time』の結果範囲で、"module:"を含む場合は、その範囲内で記載のあるsql文を一行で抽出したいと思っています。

txtから抽出したい値

① select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order# (1行で)

②  select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_
size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, a
vgcln, minimum_enc, maximum_enc from hist_head$ where obj#=:1 and intcol#=:2(1行で)

sample.txt中身 ※#結果1~4は実際のテキストには記載はありません。

SQL order by Elapsed time #結果1
-------------------------
100  100  100
module:
select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order#

SQL order by psychical time #結果2
-------------------------
300  300  300
module:
select p_obj#, nvlp_obj#=obj#(+) order by order#

SQL order by Elapsed time  #結果3
-------------------------
200  200  200
module:
select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_
size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, a
vgcln, minimum_enc, maximum_enc from hist_head$ where obj#=:1 and intcol#=:2

SQL order by Elapsed time  #結果4
-------------------------
Elapsed:
200  200  200
select ringo ringo ringo 
mikan mikan mikan mikan

~~結果5,6,7と続く(上記以外のパターンの情報もあり)

試した事

色々考えて行いましたが全く上手くいきません。。
本当にお手上げ状態です。。。。
条件に当てはまる最初の1行しか抽出できません。。
ファイル内全体で条件に当てはまる物をすべて抽出したいのですが。。
(※他にもたくさん試しましたが以下のコードが一番実現したかった事に近かったです。
他に試した内容が必要な場合はコメントください。)

import re

filepath = 'C:\\Users\\Desktop\\新しいフォルダー\\sample.txt'

def extract_text_in_file(filepath, pattern_prev, pattern_next):
    extracted_text_array = []
    with open(filepath, "r", encoding="utf-8") as f:
        is_extracting = False
        for line in f:
            line = line.rstrip()
            if not is_extracting:
                if re.match(pattern_prev, line):
                    is_extracting = True
                continue
            if re.match(pattern_next, line):
                break
            extracted_text_array.append(line.rstrip())

    return extracted_text_array

pattern_prev = r'Module:'
pattern_next = r'^$'
extracted_text_array = extract_text_in_file(filepath, pattern_prev, pattern_next)
# for extracted_text in extracted_text_array:
#     print(extracted_text)
print(' '.join(extracted_text_array))
結果
select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order#

抽出方法にお詳しい方、実現する為にはどんなコードを書けば条件に一致するすべての値を取得できるでしょうか?
(※本当に初心者です。。googleでもずっと調べていますが解決方法がわかりません。
アイデアやヒントでも構いませんのでコメントいただけるとありがたいです。。)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • polpop

    2020/07/19 04:38 編集

    can110さん申し訳ございません。
    結果3の「200 200 200」の位置ですが、私の記載ミスでした。。。
    修正しました。混乱させてしまい申し訳ございませんでした。

    キャンセル

  • polpop

    2020/07/19 04:48 編集

    1.結果の範囲、開始と終了位置、とくに終了位置がどのようなルールで決まるかが不明確だと思います。
    →実装はできていませんが、結果間の改行を終了位置にできないかと考えておりました。。

    キャンセル

  • can110

    2020/07/19 05:08 編集

    結果間に必ず改行が1行分入っているのが保証されていればそれでよいかと思われます。
    できれば修正依頼欄に書かれたルールを整理して質問本文に記載ください。
    そのさい、テキストファイルの内容を何も知らない人間に処理させるためのマニュアルだと仮定して記載してみてください。この「マニュアル」ができて初めてコンピュータにも同じことをさせることができます。

    キャンセル

回答 2

+1

とりあえず挙げられた要件を満たすであろう、できるだけシンプルなルールを考えてコード化しました。
参考まで。

# ファイルから読込済みのテストデータ
s = """SQL order by Elapsed time #結果1
-------------------------
100  100  100
module:
select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order#

SQL order by psychical time #結果2
-------------------------
300  300  300
module:
select p_obj#, nvlp_obj#=obj#(+) order by order#

SQL order by Elapsed time  #結果3
-------------------------
200  200  200
module:
select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_
size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, a
vgcln, minimum_enc, maximum_enc from hist_head$ where obj#=:1 and intcol#=:2

SQL order by Elapsed time  #結果4
-------------------------
Elapsed:
200  200  200
select ringo ringo ringo 
mikan mikan mikan mikan"""

for result in s.split('\n\n'): # 空行で「結果」ごとに分割
    if result.startswith('SQL order by Elapsed time'): # 「結果」がこの文字列で始まるもののみ
        module = result.split('module:') # 「module:」で前後を切り分ける。
        if len(module) >= 2:
            sql = module[1] # 後ろが「SQL文」
            sql = module[1].replace('\n', '') # 複数行の「SQL文」を1行にまとめる
            print(sql)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

要件が複雑なのでクラスを使います:

import re

class Scanner:
    def __init__(self, pattern_prev, pattern_next):
        self.pattern_elapsed = re.compile(r"SQL\sorder\sby\sElapsed\stime")
        self.pattern_prev = re.compile(pattern_prev)
        self.pattern_next = re.compile(pattern_next)
        self.extracted_text_array = []
        self.is_elapsed = False
        self.is_extracting = False
        self.sql = []

    def scan(self, line):
        """
        状態によって行う処理を切り替えて実行します
        """
        line = line.rstrip()
        if not self.is_elapsed:
            self.search_elapsed(line)
            return
        if not self.is_extracting:
            self.search_prev(line)
            return
        if self.pattern_next.match(line):
            self.emit_sql()
            return
        self.sql.append(line.rstrip())

    def search_elapsed(self, line):
        """
        Elapsed stime の行が見つかるまでひたすら読み飛ばします
        Elapsed stime の行が見つかった場合、読み飛ばしをやめるために状態を変更します
        """
        if self.pattern_elapsed.match(line):
            self.is_elapsed = True

    def search_prev(self, line):
        """
        pattern_prev の行が見つかるまでひたすら読み飛ばします
        pattern_prev の行が見つかった場合、読み飛ばしをやめるために状態を変更します
        """
        if self.pattern_prev.match(line):
            self.is_extracting = True

    def emit_sql(self):
        """
        仮の list に読み貯めた複数行の SQL 文を 1 行文字列に変換して
        結果の list に追加し、仮の list をクリアします
        再び Elapsed stime の行が見つかるまでひたすら読み飛ばすために状態をもとに戻します
        """
        self.is_elapsed = False
        self.is_extracting = False
        self.extracted_text_array.append(' '.join(self.sql))
        self.sql = []


def extract_text_in_file(filepath, pattern_prev, pattern_next):
    scanner = Scanner(pattern_prev, pattern_next)
    with open(filepath, "r", encoding="utf-8") as f:
        for line in f:
            scanner.scan(line)

    return scanner.extracted_text_array

filepath = 'C:\\Users\\Desktop\\新しいフォルダー\\sample.txt'

pattern_prev = r'module:'
pattern_next = r'^$'
extracted_text_array = extract_text_in_file(filepath, pattern_prev, pattern_next)
for extracted_text in extracted_text_array:
    print(extracted_text)

※ sample.txt で module: の文字列が小文字だったので、コードでも module: を小文字にしました

実行結果:

$ python test.py
select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order#
select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_ size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, a vgcln, minimum_enc, maximum_enc from hist_head$ where obj#=:1 and intcol#=:2

ポイントは、pattern_next にマッチしたとき break で処理を終わらせずに、
is_extracting を False にして抽出モードをオフにして、
continue で再びループの初めに戻り、繰り返しを続けることです

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/19 18:14 編集

    y_shinodaさん
    至れり尽くせり本当にありがとうございます。
    もう一つだけ教えて頂きたいのですが、仮に以下の様に一つの結果範囲内に2つのSQLが存在する場合も取得する事は可能でしょうか。。?
    最後に私自身勉強の為、大変恐れ入りますがご回答いただけると幸いです。
    よろしくお願い致します
    ---------------------------------------------------------------
    SQL order by Elapsed time #結果1
    -------------------------
    100 100 100
    module:
    select owner#,name,namespace,remoteowner,linkname,p_timestamp,p_obj#, nvlp_obj#=obj#(+) order by order#

    999 99 99
    module:
    select ownerasdjd from dhdada #ここも出力したい

    SQL order by psychical time #結果2
    -------------------------
    300 300 300
    module:
    select p_obj#, nvlp_obj#=obj#(+) order by order#

    キャンセル

  • 2020/07/19 18:21

    コードを変更する必要があります

    今、pattern_next を見つけたら
    を見つけたら
    self.is_extracting = False
    してますけど、それはやめて、
    "SQL order by psychical time" が見つかったら
    self.is_extracting = False
    にするようにすればいけるような気がします
    (やってみないとわかりませんが)

    キャンセル

  • 2020/07/19 18:31 編集

    y_shinodaさん
    ご回答いただきありがとうございます。
    頂いたヒントを元に自分で改修してみます。
    pythonを始めて1か月程度ですが学習がもっと楽しくなりました!
    本当に感謝致します!
    この度はありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る