C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

フォルダを監視してCSVを自動読み込み

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■89631 / inTopicNo.1)  フォルダを監視してCSVを自動読み込み
  
□投稿者/ 河童 (33回)-(2018/12/11(Tue) 16:21:20)

分類:[C#] 

いつも大変お世話になっております。

環境:Windows7 32bit VS2010 C#

フォルダを監視して、そのフォルダにCSVファイルがあった場合に
自動的にDBにデータを登録したいです。

フォーム画面にあるボタンをクリックしたときには、
下記の処理は実行できます。
・フォルダの有無チェック
・CSVファイルの有無チェック
・CSVファイルの読み込み
・DBに登録

分からないことは、
どの様にずっとフォルダを監視するのかと
ファイルを自動的に読み込ませる部分です。

ご教示、
よろしくお願いいたします。


引用返信 編集キー/
■89632 / inTopicNo.2)  Re[1]: フォルダを監視してCSVを自動読み込み
□投稿者/ 774RR (647回)-(2018/12/11(Tue) 16:30:56)
C# なら FileSystemWatcher
https://dobon.net/vb/dotnet/file/filesystemwatcher.html
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.filesystemwatcher

イベントが来るのでイベントの中でファイルを読めばよい感じ。

引用返信 編集キー/
■89633 / inTopicNo.3)  Re[2]: フォルダを監視してCSVを自動読み込み
□投稿者/ 河童 (34回)-(2018/12/11(Tue) 19:28:47)
No89632 (774RR さん) に返信

774RRさん、お返事ありがとうございます。

フォームにあるボタンに処理を追加してみました。

フォルダを監視することができたのですが、
一度メッセージが表示されると監視が中断されます。

監視を継続させるにはどのようにすればよいでしょうか?
その時は監視を中断させるボタンも必要になってくると思うのですが?

・監視の開始
・何かしらのエラーが発生するまで監視を継続
・監視の終了


        private void btn_Click(object sender, EventArgs e)
        {

            FileSystemWatcher watcher =new FileSystemWatcher();

            //監視するディレクトリを指定
            watcher.Path = @"C:\csvroot";

            //*.txtファイルを監視、すべて監視するときは""にする
            watcher.Filter = "*.txt";
            //ファイル名とディレクトリ名と最終書き込む日時の変更を監視
            watcher.NotifyFilter = NotifyFilters.FileName 
                                 | NotifyFilters.DirectoryName
                                 | NotifyFilters.LastWrite;
            //サブディレクトリも監視
            watcher.IncludeSubdirectories = true;

            //必要に応じてバッファサイズを変更
            //watcher.InternalBufferSize = 4096

            //同期的に監視を開始する
            WaitForChangedResult changedResult = watcher.WaitForChanged(WatcherChangeTypes.All);

            if (changedResult.TimedOut)
            {
                CmnMsgBox.Show("タイムアウトしました。", 2);
                return;
            }

            //変更があったときに結果を表示する
            switch (changedResult.ChangeType)
            {
                case WatcherChangeTypes.Changed:
                    CmnMsgBox.Show(
                        "ファイル 「" + changedResult.Name + "」が変更されました。", 2);
                    break;
                case WatcherChangeTypes.Created:
                    CmnMsgBox.Show(
                        "ファイル 「" + changedResult.Name + "」が作成されました。", 2);
                    break;
                case WatcherChangeTypes.Deleted:
                    CmnMsgBox.Show(
                        "ファイル 「" + changedResult.Name + "」が削除されました。", 2);
                    break;
                case WatcherChangeTypes.Renamed:
                    CmnMsgBox.Show(
                        "ファイル 「" + changedResult.OldName +
                        "」の名前が「" + changedResult.Name +
                        "」に変更されました。", 2);
                    break;
            }

        }

引用返信 編集キー/
■89634 / inTopicNo.4)  Re[3]: フォルダを監視してCSVを自動読み込み
□投稿者/ Hongliang (729回)-(2018/12/11(Tue) 20:08:46)
https://dobon.net/vb/dotnet/file/filesystemwatcher.html
なお、記事の末尾にありますが、フォームデザイナでも扱えます。
引用返信 編集キー/
■89637 / inTopicNo.5)  Re[4]: フォルダを監視してCSVを自動読み込み
□投稿者/ 774RR (648回)-(2018/12/12(Wed) 08:50:16)
89633 のコードは FileSystemWatcher watcher; がイベントハンドラの局所変数だから、
- イベントハンドラを抜けたら gc される
- イベントハンドラを抜けない限り次の処理がされることはない
というあたりの理解はちゃんとある、んだよね?

スタートボタンで開始し、ストップボタンで停止する、ができるためには
- watcher は Form1 のメンバにしないといけない
- watcher の初期化は Form1() コンストラクタなり Form1_Load() ハンドラなりで行うべき
- スタートボタンが行うべき処理は EnableRaisingEvents = true; のみ
- ストップボタンが行うべき処理は EnableRaisingEvents = false; のみ
- Form1 を閉じる際にイベントハンドラが喧嘩しないようにする必要がある
なわけで、そういう風にコード書かなきゃならないよ。

(んで STA の罠にはまる、っと)

引用返信 編集キー/
■89638 / inTopicNo.6)  Re[5]: フォルダを監視してCSVを自動読み込み
□投稿者/ 河童 (35回)-(2018/12/12(Wed) 09:42:33)
No89637 (774RR さん) に返信

- イベントハンドラを抜けたら gc される
gcは、ガーベジコレクション?
不要になったメモリの領域を解放する機能ですね。
こういう仕組みがあるんですね。

- イベントハンドラを抜けない限り次の処理がされることはない
というあたりの理解はちゃんとある、んだよね?
えぇと、これが同期か非同期かという事かな。
なるほど、今の処理は同期処理で
途中で監視を中断させることはできないという事か。


スタートボタンで開始し、ストップボタンで停止する、ができるためには
- watcher は Form1 のメンバにしないといけない
- watcher の初期化は Form1() コンストラクタなり Form1_Load() ハンドラなりで行うべき
- スタートボタンが行うべき処理は EnableRaisingEvents = true; のみ
- ストップボタンが行うべき処理は EnableRaisingEvents = false; のみ
- Form1 を閉じる際にイベントハンドラが喧嘩しないようにする必要がある

まだ理解できていない部分がありますが、
とりあえず、処理を書いてみます。

ちなみに、STAとは、何の略ですか?
引用返信 編集キー/
■89640 / inTopicNo.7)  Re[6]: フォルダを監視してCSVを自動読み込み
□投稿者/ 774RR (649回)-(2018/12/12(Wed) 10:10:06)
Form 系の部品は「 new したスレッドでのみ使うことができる」という仕様になっていて、
この仕様を Single Thread Apartment (STA) と呼ぶ慣例になってます。
んで Form 部品は(デザイナを使う限り)いわゆる主 UI スレッド内部で new されます。
なので主 UI スレッド以外から label1.Text = "Hoge"; みたいなことをすると失敗します。
(この辺詳しく知りたいならわんくま勉強会へようこそ)

例えば通信を別スレッドで行うマルチスレッドプログラミングで、
- 通信が来た (通信スレッド内部で判断) から
- 画面を更新 (通信スレッド内部で更新) したい
なんてのはよくある要望なわけですが、この画面更新の際に
Form 部品を通信スレッドから操作する行為は禁じられていて、ひと手間かける必要があります。
どこにどんな手間をかけるかは要望というか仕様次第。
http://www.atmarkit.co.jp/fdotnet/dotnettips/287winfilewatch/winfilewatch.html

dobon さんところのサンプルそこそこよくできているので
( new をスタートボタンハンドラ内で行っているのはオイラ個人的には気に入らないけど)
真似すれば5分もあれば監視するだけのプログラムは書けちゃいます。お試しあれ。


引用返信 編集キー/
■89650 / inTopicNo.8)  Re[7]: フォルダを監視してCSVを自動読み込み
□投稿者/ 河童 (37回)-(2018/12/13(Thu) 10:17:21)
No89640 (774RR さん) に返信

おはようございます。
「SynchronizingObject」というのキーになるのかな。

一応監視するだけのプログラムを書いてみました。


監視解除ボタンを連続で押下した時、

watcher.EnableRaisingEvents = false;

の部分でエラーが発生しました。
「NullReferenceExcption はハンドルされませんでした。」
「オブジェクト参照がオブジェクト インスタンスに設定されていません。」

だから監視の状態を判定する変数を作って
監視中なら解除するように設定すると直りました。


エラーが発生した時の処理も考えようと思っています。
フォームを閉じる際にイベントハンドラが喧嘩しないようにする必要があるとありますが、
どの様な処理の時を気を付ければ良いでしょうか?
今のところエラーは発生していないのですが。

よろしくお願いいたします。



namespace FileWatcher
{
    public partial class F01_FileUpload : Form
    {

        //CSVファイルの保管ディレクトリ
        string CsvDerectoryPath = @"C:\csvroot";

        //ディレクトリの監視
        private FileSystemWatcher watcher = null;

        //監視状態(0:解除中、1:監視中)
        string Status = "";
        string Status_0 = "監視解除";
        string Status_1 = "監視中";

        public F01_FileUpload()
        {
            InitializeComponent();
        }

        private void F01_FileUpload_Load(object sender, EventArgs e)
        {

            if (watcher != null) return;

            watcher = new FileSystemWatcher();
            //監視するディレクトリを指定
            watcher.Path = CsvDerectoryPath;

            //最終アクセス日時、最終更新日時、ファイル、フォルダ名の変更を監視する
            watcher.NotifyFilter =
                (System.IO.NotifyFilters.LastAccess
                | System.IO.NotifyFilters.LastWrite
                | System.IO.NotifyFilters.FileName
                | System.IO.NotifyFilters.DirectoryName);

            //すべてのファイルを監視
            watcher.Filter = "";

            //UIのスレッドにマーシャリングする
            watcher.SynchronizingObject = this;

            //イベントハンドラの追加
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.Created += new FileSystemEventHandler(watcher_Changed);
            watcher.Deleted += new FileSystemEventHandler(watcher_Changed);
            watcher.Renamed += new RenamedEventHandler(watcher_Renamed);

            //監視を開始する
            watcher.EnableRaisingEvents = true;

            Status = "1";
            lblStatus.Text = Status_1;
            lblStatus.BackColor = Color.LimeGreen;

        }

        private void btnStart_Click(object sender, EventArgs e)
        {

            if (Status != "1")
            {
                //監視を開始する
                watcher.EnableRaisingEvents = true;

                lblStatus.Text = Status_1;
                lblStatus.BackColor = Color.LimeGreen;
            }
            
        }

        private void btnEnd_Click(object sender, EventArgs e)
        {
            if (Status != "0")
            {
                //監視を終了
                watcher.EnableRaisingEvents = false;
                watcher.Dispose();
                watcher = null;

                Status = "0";
                lblStatus.Text = Status_0;
                lblStatus.BackColor = Color.DarkOrange;
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {

            if (Status != "0")
            {
                //監視を終了
                watcher.EnableRaisingEvents = false;
                watcher.Dispose();
                watcher = null;
            }

            //アプリケーションを終了する
            Application.Exit();

        }

        //イベントハンドラ
        private void watcher_Changed(System.Object source,
            System.IO.FileSystemEventArgs e)
        {
            switch (e.ChangeType)
            {
                case System.IO.WatcherChangeTypes.Changed:
                    CmnMsgBox.Show(
                        "ファイル 「" + e.FullPath + "」が変更されました。", 2);
                    break;
                case System.IO.WatcherChangeTypes.Created:
                    CmnMsgBox.Show(
                        "ファイル 「" + e.FullPath + "」が作成されました。", 2);
                    break;
                case System.IO.WatcherChangeTypes.Deleted:
                    CmnMsgBox.Show(
                        "ファイル 「" + e.FullPath + "」が削除されました。", 2);
                    break;
            }
        }

        private void watcher_Renamed(System.Object source,
            System.IO.RenamedEventArgs e)
        {
            CmnMsgBox.Show(
                "ファイル 「" + e.FullPath + "」の名前が変更されました。", 2);
        }

           
    }
}

引用返信 編集キー/
■89652 / inTopicNo.9)  Re[8]: フォルダを監視してCSVを自動読み込み
□投稿者/ とっちゃん (556回)-(2018/12/13(Thu) 10:51:58)
No89650 (河童 さん) に返信
> 監視解除ボタンを連続で押下した時、
>
> watcher.EnableRaisingEvents = false;
>
> の部分でエラーが発生しました。
> 「NullReferenceExcption はハンドルされませんでした。」
> 「オブジェクト参照がオブジェクト インスタンスに設定されていません。」
<<中略>>

監視解除した時に以下のコードを動かしています。

> //監視を終了
> watcher.EnableRaisingEvents = false; // 連続で呼び出したら、ここで NullReferenceExcption 発生...
> watcher.Dispose();
> watcher = null;

ちなみに、ですがここに挙げたプログラムコードで
監視解除後に再び監視を開始してみてください。

> //監視を開始する
> watcher.EnableRaisingEvents = true;

おそらく、ここで同じように NullReferenceExcption が発生すると思います。

原因となるコードを書いておいたのでそれをもとになぜそうなるかを考えてみてください。

フォルダやファイルの監視は単純な話ではないので(検出した後どうするかで、千差万別なパターンがあるため)
具体的にどうするのがいいかについては、しっかりとした仕様がないので言及しません。


引用返信 編集キー/
■89659 / inTopicNo.10)  Re[9]: フォルダを監視してCSVを自動読み込み
□投稿者/ 河童 (38回)-(2018/12/13(Thu) 13:05:20)
No89652 (とっちゃん さん) に返信

とっちゃん さん、ありがとうございます。

>>//監視を開始する
>>watcher.EnableRaisingEvents = true;
> 
> おそらく、ここで同じように NullReferenceExcption が発生すると思います。

解除した後、再度監視を実行するとエラーが発生しました。

解除した時に
Disposeしていたのが原因かな。

774RR さんが前にこう言われていました。
- スタートボタンが行うべき処理は EnableRaisingEvents = true; のみ
- ストップボタンが行うべき処理は EnableRaisingEvents = false; のみ

プログラムを終了するときに、Disposeする。

 
> フォルダやファイルの監視は単純な話ではないので(検出した後どうするかで、千差万別なパターンがあるため)
> 具体的にどうするのがいいかについては、しっかりとした仕様がないので言及しません。
これでフォルダの監視のONとOFFの切り替えはできるようになりました。
続けて、CSVの読み込み処理を書いていきたいと思います。
監視フォルダに作成されたフォルダ(サブフォルダも含む)の中にある
CSVファイルを読み込み、DBに登録、登録後にフォルダごとファイル保管フォルダに移動させる。

        private void btnStart_Click(object sender, EventArgs e)
        {

            if (Status != "1")
            {

                //監視を開始する
                watcher.EnableRaisingEvents = true;

                Status = "1";
                lblStatus.Text = Status_1;
                lblStatus.BackColor = Color.LimeGreen;
            }
            
        }

        private void btnEnd_Click(object sender, EventArgs e)
        {
            if (Status != "0")
            {
                //監視を終了
                watcher.EnableRaisingEvents = false;

                Status = "0";
                lblStatus.Text = Status_0;
                lblStatus.BackColor = Color.DarkOrange;
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {

            if (Status != "0")
            {
                //監視を終了
                watcher.EnableRaisingEvents = false;
                watcher.Dispose();
                watcher = null;
            }

            //アプリケーションを終了する
            Application.Exit();

        }



引用返信 編集キー/
■89661 / inTopicNo.11)  Re[9]: フォルダを監視してCSVを自動読み込み
□投稿者/ 774RR (650回)-(2018/12/13(Thu) 13:10:12)
dobon さんとこのサンプルは FileSystemWatcher を「自前のコードで使う」ためのもので
こんなプロパティがありますよ、こうやって使うことができますよ Dispose が必要っすよ
ってなあたりを目に見える形で示すのが目的。
(そうでないとサンプルとして役に立たない)
だから「手で書いてあるコード」がやたらと多くて難しく見えるっす。

既にアドバイスある通りデザイナで FileSystemWatcher を配置すれば、デザイナ画面だけで
- Path や EnableRaisingEvents の初期値が設定できて、
- イベントハンドラもダブルクリックだけで作ってくれて、
- ついでに SynchronizingObject の設定も忘れずに行ってくれて、
手書きするコードなんてほとんどないっす。

デザイナで fileSystemWatcher1 btnStart btnStop を配置して、
デザイナで各部品のプロパティ・イベントを適切に設定すると、
Form1.cs で手書きしなきゃならないのは各ハンドラで1行だけになって、こんな感じ

using System.Diagnostics; // は手書き
namespace Watch
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
        {
            Debug.WriteLine("Change " + e.FullPath);
        }

        private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
        {
            Debug.WriteLine("Create " + e.FullPath);
        }

        private void fileSystemWatcher1_Deleted(object sender, System.IO.FileSystemEventArgs e)
        {
            Debug.WriteLine("Delete " + e.FullPath);
        }

        private void fileSystemWatcher1_Renamed(object sender, System.IO.RenamedEventArgs e)
        {
            Debug.WriteLine("Rename " + e.FullPath);
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            fileSystemWatcher1.EnableRaisingEvents = true;
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            fileSystemWatcher1.EnableRaisingEvents = false;
        }
    }
}

ね、5分もかからないでしょ。

デザイナ画面でどんな操作をしないといけないかは掲示板で説明するには面倒すぎるのでパス。
普通にデザイナを使いこなせるなら簡単そのもの。

引用返信 編集キー/
■89667 / inTopicNo.12)  Re[10]: フォルダを監視してCSVを自動読み込み
□投稿者/ 河童 (39回)-(2018/12/13(Thu) 16:44:27)
No89661 (774RR さん) に返信

774RR さん、ありがとうございます。
そういう意味だったんですね。

いろんなツールが用意されているんですね。

今回も勉強になりました。

次はイベントハンドラに
CSVファイルの読み込み処理を書いていきます。

また何かあると思いますが、
よろしくお願いいたします。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ