今回はJavaでテキストファイルから特定文字列を含む行を除外してファイル出力するコードを作ったので紹介。
コマンドライン引数で複数のキーワードを指定して除外できるようにした。
作成の経緯
このブログのアクセスログを分析する際、いつもダウンロードしたcsvをそのままExcelで開いているが、ひと月あたり数万件のデータがあるので私の非力なマシンではちょっと厳しくなってきた。
特に、検索エンジンからの流入を除いた被リンクからのアクセスだけを分析したい場合、google、yahoo、bing、searchといったキーワードをあらかじめ除外しておくとデータ件数が三分の一くらいまで減るのでExcelで扱うのが楽になる。
sedかawkでやれば早いけど、インストールしてないうえ、使い方を忘れたので面倒くさい。そこは日曜プログラマーなので、たまには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:ファイルの読み書きに失敗しました。"); } } }
使い方
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版は一瞬で完了したので、数十万件になってくるとだいぶ差が出てきそう。
参考書籍
- 作者: 高橋麻奈
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2016/08/31
- メディア: 単行本
- この商品を含むブログを見る
今回はリファレンスがわりに使ったけど、基本的にLesson1から順に進めていくタイプの本なので、別途リファレンスが欲しくなった。