t-hom’s diary

主にVBAネタを扱っているブログです。

Javaでテキストファイルから特定文字列を含む行を除いてファイル出力

今回はJavaでテキストファイルから特定文字列を含む行を除外してファイル出力するコードを作ったので紹介。
コマンドライン引数で複数のキーワードを指定して除外できるようにした。

作成の経緯

このブログのアクセスログを分析する際、いつもダウンロードしたcsvをそのままExcelで開いているが、ひと月あたり数万件のデータがあるので私の非力なマシンではちょっと厳しくなってきた。
特に、検索エンジンからの流入を除いた被リンクからのアクセスだけを分析したい場合、google、yahoo、bing、searchといったキーワードをあらかじめ除外しておくとデータ件数が三分の一くらいまで減るのでExcelで扱うのが楽になる。

sedawkでやれば早いけど、インストールしてないうえ、使い方を忘れたので面倒くさい。そこは日曜プログラマーなので、たまにはJavaのリハビリがてら自分で作ってみることに。(もっと面倒くさいだろうというツッコミはナシで)

できたコード

import java.io.*;

class Filter
{
    public static void main(String[] args)
    {
        if(args.length < 1){
            System.out.println("使用例:java Filter [fileName] [Exclude Text1] [Exclude Text2] ...");
            System.exit(1);
        }
        try {
            BufferedReader br =
                new BufferedReader
                    (new FileReader(args[0]));

            PrintWriter pw = new PrintWriter
                (new BufferedWriter(new FileWriter("out.txt")));

            String str;
            boolean flag;
            while((str = br.readLine()) != null){
                flag = true;
                for(int i=1;i<args.length;i++){
                    flag = flag && str.indexOf(args[i]) == -1;
                }

                if(flag) {
                    pw.println(str);
                }
            }
            br.close();
            pw.close();
        }
        catch(IOException e){
            System.out.println("Error:ファイルの読み書きに失敗しました。");
        }
    }
}

コンパイル

javac Filter.java

Eclipseを使うほどのコードでもないので。。

使い方

java Filter [入力ファイル名] [除外テキスト1] [除外テキスト2] ...

例) java Filter 101-2017-05.csv google yahoo bing search

このように除外テキストはいくつでも指定できる。

所感

例外のケアが雑だけど、まぁ自分ひとりで特定の目的に限った利用なのでこれで十分。
しばらくVBAばかりやってたので、等価比較を「=」で書いてしまう癖が出てハマった。
Javaの等価比較は「==」

工夫した点としては複数キーワードの除外判定ループ内で「flag = flag && 条件」としているところ。
これは普段VBAでもよく使ってるパターンで、複数条件を束ねるのに便利。
論理積の性質で、ループ内で1度でもfalseが出ると以降flagはtrueが代入される次の周回までfalseに固定される。

地味に基本情報などのコンピューターサイエンス系の知識が活きるので、にわかプログラマーとの差別化ができてうれしい。

使ってみた感想としては、やはりJavaは動作の速さが印象的。一瞬で処理が終わるので気持ちいい。※初回実行はJava VM起動があるのでほんの少し待たされるけれど。
VBAが遅いんじゃない。あなたのコードが遅いんだ。」なんて巷で良く言われてるけれど、あれは半分本当で半分嘘。正しくは「あなたのコードも遅いけど、VBAそもそも遅い。」だ。
ただしデータ量が少ない場合はVBAでも十分速いのでたかだか数万件ならVBAでも問題なかったかもしれない。

ちなみにVBAで書くと

こうなる。

Sub FilterText()
    '※サンプルなのでダイアログ等使わずに定数で代用
    Const BASE_PATH = "C:\Users\thom\java\", _
        INPUT_FILE_NAME = "101-2017-05.csv", _
        OUTPUT_FILE_NAME = "out2.txt", _
        EXCLUDE_WORDS = "google yahoo bing search"
        
    Dim reader As TextStream
    Dim writer As TextStream
    
    With New FileSystemObject
        Set reader = .OpenTextFile(BASE_PATH & INPUT_FILE_NAME, ForReading)
        Set writer = .OpenTextFile(BASE_PATH & OUTPUT_FILE_NAME, ForWriting, True)
    End With
    Dim str As String, flag As Boolean, ex As String
    Do Until reader.AtEndOfLine
        str = reader.ReadLine
        flag = True
        For Each ex In Split(EXCLUDE_WORDS)
            flag = flag And InStr(str, ex) = 0
        Next
        If flag Then writer.WriteLine str
    Loop
    reader.Close
    writer.Close
End Sub

私のマシンだと約7万行に対して5秒くらい。Java版は一瞬で完了したので、数十万件になってくるとだいぶ差が出てきそう。

参考書籍

こちらは私が初めてJavaを学んだ書籍の第六版。

今回はリファレンスがわりに使ったけど、基本的にLesson1から順に進めていくタイプの本なので、別途リファレンスが欲しくなった。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。