Help us understand the problem. What is going on with this article?

メモ : 【Java】監視しているディレクトリにファイルが入ったら処理を行う

背景

前回、CSVファイルを加工して出力するというのを書いたんですが、
対象のディレクトリにCSVファイルが入ってきたらファイルを加工するっていう流れにしたいなー
と思ったので、コードを書き残します。

監視.png

まずはディレクトリの監視

- WatchService 使い方メモ
- Javaでファイルが作成されるのを待って、作成を検知してから次の処理を行う
:grinning: こちらのページを参考にさせて頂きました。ありがとうございます。

test.java
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.Watchable;

import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.WatchEvent.*;

public class test {

    public static void main(String[] args) throws Exception {
        Path dir = Paths.get("C://develop//BOX");
        WatchService watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

        for (;;) {
            WatchKey watchKey = watcher.take();
            for (WatchEvent<?> event: watchKey.pollEvents()) {
                if (event.kind() == OVERFLOW) continue;

                WatchEvent<Path> ev = cast(event);
                Path name = ev.context();
                Path child = dir.resolve(name);
                System.out.format("%s: %s\n", event.kind().name(), child);
            }
            watchKey.reset();
        }
    }

    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }
}

では、C:\developBOX というディレクトリを作成して、
1.png

任意の場所で test.java をコンパイルしてクラスを実行してみます。( ※ 今回ワタシは D:\で実行してます)
2.png

わーっ!コンパイル時にエラーが出ちゃったよって方は :beginner:こちら 参考になります。

では、classファイルを実行します。適当なファイルをBOXディレクトリに入れてみます。
3.png

おーっ‼ディレクトリに入れた さんぷる.txt が
ENTRY_CREATE(作成) と ENTRY_MODIFY(変更) と表示されました。
4.png

ファイルをディレクトリから削除するとENTRY_DELETE(削除)が表示されています。
5.png

実際動かしてからの方がわかるなーと思うので先に動かした時の画像貼りましたが
test.javaにはこんなのが書いてます。(コメントはワタシの解釈で書いてるので間違えてるかも…。)
2行目の WatchService watcher = FileSystems.getDefault().newWatchService(); というのは
決まった言い回しらしいので深く考えずそのまま使います。

Path dir = Paths.get("C://develop//BOX");                            //"dir"に監視対象のディレクトリを指定して
WatchService watcher = FileSystems.getDefault().newWatchService();   //WatchServiceを"watcher"という名前で作成
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);     //"dir"内で監視するイベントを指定(作成,削除,変更)
for (;;) {                                                           //イベントを待機するために無限ループを実装
   WatchKey watchKey = watcher.take();                               //watcherから監視キー"watchKey"を取得する
   for (WatchEvent<?> event: watchKey.pollEvents()) {                //pollEventsメソッドでWatchEventが格納されたListが取得出来る。
       if (event.kind() == OVERFLOW) continue;                       //"OVERFLOW"はイベントが消失or破棄されたのを示す特殊な監視イベント

        WatchEvent<Path> ev = cast(event);
        Path name = ev.context();                                    //"name"にイベント・コンテキストで取得してファイル名を設定       
        Path child = dir.resolve(name);                              //resolveメソッドでフルパス("dir"のパスに"name"を追加したもの)を設定
        System.out.format("%s: %s\n", event.kind().name(), child);   //出力時は【イベント名:ファイルのフルパス】で出力する指示
   }
   watchKey.reset();                                                 //監視キーをリセットする(WatchKeyの状態をREADYに戻すため)

csvを加工する処理を組み合わせます

前回のコード [ 読み込んだcsvを加工(条件で抽出、変更)して出力する ]
test.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class test{

    public static void main(String[] args) {
        process("20180712.csv",  ":00,","output.csv");
    }

    public static void process(String read_file, String searchString, String output_file){

        try(FileReader fr = new FileReader(read_file);BufferedReader br = new BufferedReader(fr);
            FileWriter fw = new FileWriter(output_file);BufferedWriter bw = new BufferedWriter(fw)){

            String line;
            while ((line = br.readLine()) != null) {
                Pattern p = Pattern.compile(searchString);
                Matcher m = p.matcher(line);
                if (m.find()){
                    String[] csvArray;
                    csvArray = line.split(",");
                    String time  = csvArray[0];
                    float press = Float.parseFloat(csvArray[1])%10000;
                    String pressure = String.format("%.2f", press);
                    String temperature  = csvArray[2];
                    String outputLine = String.join(",",time,pressure,temperature);
                    bw.write(outputLine);
                    bw.newLine();
                }else{ 
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

前回のコードと上で書いたコードは大きく変えてません。ほぼ結合しただけです。

test.java
import java.nio.file.FileSystems;      //ディレクトリ監視に必要
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.Watchable;
import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.WatchEvent.*;

import java.io.BufferedReader;        //csvを加工して出力するのに必要
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class test {

    public static void main(String[] args) throws Exception {

        Path dir = Paths.get(System.getenv("CSV_DIR"));       //今回は 監視するディレクトリを環境変数"CSV_DIR"に設定
        WatchService watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher, ENTRY_CREATE);                  //今回は ENTRY_CREATEだけ検知すればOK

        for (;;) {
            WatchKey watchKey = watcher.take();
            for (WatchEvent<?> event: watchKey.pollEvents()) {
                if (event.kind() == OVERFLOW) continue;              
                WatchEvent<Path> ev = cast(event);
                Path name = ev.context();
                Path child = dir.resolve(name);
                String newfile =String.format("%s", child);

                process(newfile, ":00,","D://tedkuma//★"+name); //processで読込むファイルに検知したファイルのフルパスを渡す
                System.out.println("Output success:"+name);
            }
            watchKey.reset();
        }  
    }

    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }

    public static void process(String read_file, String searchString, String output_file){ //以下は前回のをそのまま流用

        try(FileReader fr = new FileReader(read_file);BufferedReader br = new BufferedReader(fr);
            FileWriter fw = new FileWriter(output_file);BufferedWriter bw = new BufferedWriter(fw)){

            String line;
            while ((line = br.readLine()) != null) {
                Pattern p = Pattern.compile(searchString);
                Matcher m = p.matcher(line);
                if (m.find()){
                    String[] csvArray;
                    csvArray = line.split(",");
                    String time  = csvArray[0];
                    float press = Float.parseFloat(csvArray[1])%10000;
                    String pressure = String.format("%.2f", press);
                    String temperature  = csvArray[2];
                    String outputLine = String.join(",",time,pressure,temperature);
                    bw.write(outputLine);
                    bw.newLine();
                }else{ 
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

日本語のコメントとか★マークとかがjavaファイルの中に含まれているので
javacだけではコンパイルエラーになってしまいます :sweat:
:skull: エラー:  この文字は、エンコーディングMS932にマップできません って出てしまうので、
今回は javac -encodhing utf-8 test.java でコンパイルしています。では、実行してみます。
11.png

環境変数"CSV_DIR"は C:\develop\monitored という場所に設定しました。(今回の監視対象ディレクトリ)
この中に加工したいCSVファイルを入れてみます。
12.png

Output success:の後ろにディレクトリに入れたファイル名が表示されています。
13.png

出力先に指定したDドライブのtedkumaディレクトリを確認してみます。
ファイル名の頭に★が付いたファイルが出力されて、中のcsvも加工されています。
14.png

今回は以上です。

tedkuma
ずーっとエクセルが得意なだけの営業事務してました。プログラマーになりたいとアラフォーデビューと同時に開発のお仕事に転職。開発経験が浅いので、チートシートとして書き残しつつ、ワタシみたいな人に共有したいと記事を書いています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした