テキストファイルを最終行から検索するには?

解決


トラ  2010-02-06 11:45:21  No: 102224  IP: 192.*.*.*

テキストファイルを最終行から1行ずつ検索し、特定の条件を満たす行が見つかったら、それ以降の行を読み込み、別のファイルに書き込むという処理が行いたいです。
初心者で説明もいまいちかもしれませんが、ご教授願います。

例)
条件:最も近い日の18:00以降に記載された行
以下の内容のテキストファイルから、操作⑥以降の行を取得したいです。

08:00 操作①
19:30 操作②
22:30 操作③ 
07:00 操作④
12:00 操作⑤
21:00 操作⑥
23:00 操作⑦

編集 削除
特攻隊長まるるう  2010-02-06 14:11:26  No: 102225  IP: 192.*.*.*

現状はどこまでできているのですか?

例えば、ファイルの入出力だけなら、どんな言語でも必ず用意されていますし、
ヘルプ、参考書、サンプルでも解説されていますよね?
それをどのように改造しようとして、どこがどのようにできないかを
サンプルコードなどを提示し、具体的に説明するようにしてください。

編集 削除
なな  2010-02-07 05:06:46  No: 102226  IP: 192.*.*.*

質問の焦点がよくわからないです。
「何がしたいか」は大体わかるけど「何が知りたいのか」をはっきりさせて下さいな。
まるろうさんのおっしゃるとおり「どこまで出来ているか」を考えればおのずと知りたい事は明確になると思います。


私がやるとしたら以下のようにします、という「手順」の一例を挙げておきます。

1)テキスト全体を読み込みString型に格納。(Open For BinaryでGetを使用して一括処理)
  ※Getの引数にByte型配列の代わりにString型変数を使います
2)後方から開始し前方に到達するまで改行コードを探すループ。(InStrRev関数とか)
3)ループ内で行先頭の文字を解析して合致するか判定、見つかればループを抜ける。
4)発見した行以降を新たな文字列型に入れ直す。(Mid関数を第2引数未指定で使用とか)
5)ファイルに出力。(Open For BinaryでPutを使用して一括処理)


この方法だとメモリ上にすべて読み込んでから処理を行います。
メリットはファイルロック時間を短縮出来る事と処理の高速化が見込める事です。

その反面、ファイルがあまりに巨大だといろいろ不具合が出ます。
必要メモリが膨大になりますし、対象行が最後方に常に位置したりする条件下では
ファイルお読むオーバーヘッドが丸ごと無駄です。

対象は何かのログファイルでしょうから、対象のファイルサイズは延々増大し続けるんですよね?
実行環境の処理性能と使用できるメモリ量にもよりますけど、
ごく一般的なPCならメモリ余ってますし10〜100MB程度は楽勝なんじゃないかな。

編集 削除
トラ  2010-02-07 12:25:40  No: 102227  IP: 192.*.*.*

まるるうさん、ななさん、早急な回答ありがとうございます。
また、ご指導ありがとうございます。
対象ファイルは、ななさんがおっしゃる通りログファイルで、ファイルサイズは1GBを超えてしまうこともあります。
現状作成出来ているものは、テキストファイルを先頭行から順に読み込んで、18:00以降となる行を抽出するところまでで、当然結果は以下のようになってしまいます・・・
  19:30 操作②  ←不要
  22:30 操作③  ←不要
  21:00 操作⑥  ←必要
  23:00 操作⑦  ←必要

以下、作成したコードです。(本当に基本的な部分しか出来ておりません)
Sub main()
    '変数宣言
    Dim iPath As String       '入力ファイルパス
    Dim oPath As String       '出力ファイルパス
    Dim iTS As TextStream     '入力ファイル
    Dim oTS As TextStream     '出力ファイル
    Dim objFSO As New FileSystemObject
    Dim chkStr As String        'チェック文字
    
    'ファイル選択
    iPath = Application.GetOpenFilename("ログファイル(*.log),*.log", , "ログファイルを指定")
    If iPath = "" Then
        MsgBox "パスが空なので処理終了"
        Exit Sub
    End If
    
    '出力ファイル作成
    oPath = objFSO.GetParentFolderName(iPath) + "\出力.log"
    Set oTS = objFSO.CreateTextFile(oPath, True)
    
    'ファイル内容読み込み
    Set iTS = objFSO.OpenTextFile(iPath, ForReading)
    With iTS
        Do While .AtEndOfStream <> True
            chkStr = .ReadLine
            If StrComp(chkStr, "18:00") = 1 Then
                oTS.WriteLine (chkStr)
            End If
        Loop
        .Close
    End With
End Sub

編集 削除
はい。  2010-02-07 14:29:06  No: 102228  IP: 192.*.*.*

解決
(笑)

編集 削除
特攻隊長まるるう  2010-02-08 18:31:12  No: 102229  IP: 192.*.*.*

日付情報が無い時点で以下のように仕様上のバグが存在します。

パターン1
2010/02/07 18:10 操作①  ←不要
2010/02/08 19:30 操作②  ←必要
2010/02/08 22:30 操作③  ←必要 
パターン2
2010/02/08 18:10 操作①  ←必要
2010/02/08 19:30 操作②  ←必要
2010/02/08 22:30 操作③  ←必要 

18:10 操作①  ←どっち?
19:30 操作②  ←必要
22:30 操作③  ←必要

区別する情報がありませんので、最も近い日を取り出すこと自体が
不可能です。このバグは無視していいのですか?

編集 削除
トラ  2010-02-08 23:25:21  No: 102230  IP: 192.*.*.*

まるるうさん、ありがとうございます。
指摘して頂いたバグは無視して構いません。
昨日、以下のように一度配列に入れて、後方からループ処理で探索するような処理を作ってみましたが、軽いファイルなら出来たのですが、今日、実際のデータ(とりあえず、500MB位)で試したところ、メモリ不足で駄目でした。(ReadAllが帰ってこない)
    Set iTS1 = objFSO.OpenTextFile(iPath1, ForReading)
    AllStr1 = iTS1.ReadAll
    LineStr1() = Split(AllStr1, vbCrLf)

編集 削除
特攻隊長まるるう  2010-02-09 07:13:04  No: 102231  IP: 192.*.*.*

> 指摘して頂いたバグは無視して構いません。
どのように無視しますか?もともとの仕様を満たせないわけですが。
「最も近い日」を「1回でも18:00以前のログがあった日」に変更しますか?
つまり
2010/02/07 19:30 操作(1) ←不要
2010/02/08 08:00 操作(2) ←不要
2010/02/08 19:30 操作(3) ←必要
2010/02/09 19:31 操作(4) ←必要
2010/02/10 19:32 操作(5) ←必要
2010/02/11 19:33 操作(6) ←必要
2010/02/12 19:34 操作(7) ←必要
2010/02/13 19:35 操作(8) ←必要
2010/02/14 19:36 操作(9) ←必要
2010/02/15 19:37 操作(10) ←必要
2010/02/16 19:38 操作(11) ←必要
2010/02/17 19:39 操作(12) ←必要
2010/02/18 19:40 操作(13) ←必要
2010/02/19 19:41 操作(14) ←必要
ですね。(不要と必要は変更できません)

>メモリ不足で駄目でした。(ReadAllが帰ってこない)
そりゃまあ、そうでしょうね。
ファイルサイズが大きいので、先のサンプル通り
1行ずつ読み込むしかないですね。

「1回でも18:00以前のログが見つかる」条件まで
1行ずつ変数に記録していくようにすればいいのでは?
18:00以前のログが見つかったら変数クリア。
(最終ログが18:00以前なら該当ログデータなし)

ま、変数と書きましたが、データを複数格納できる
入れ物がいいですね。配列はVBのメモリ管理が下手で
クリアしても実行関数を抜けるまでメモリを食いつぶす
かもしれないので、Dictionary などのコレクションを
使ってみてはいかが?
(使い方は過去ログ検索等で調べてください)

編集 削除
トラ  2010-02-09 22:01:34  No: 102232  IP: 192.*.*.*

まるるうさん、何度もありがとうございます。
>そりゃまあ、そうでしょうね。
>ファイルサイズが大きいので、先のサンプル通り
>1行ずつ読み込むしかないですね。
本当お恥ずかしいですが、PGやパソコンに関してもど素人で、業務上緊急で必要となったので、慌ててバタバタしております・・・

>Dictionary などのコレクションを
>使ってみてはいかが?
調べてみます。
というか、配列以外にも似たようなものが色々あるんですね。
チャレンジしてみます。
本当、色々と教えていただき、ありがとうございました。

編集 削除