ロックせずにテキストファイルを読み取るにはどうすればよいですか?


94

Windowsサービスでログをテキストファイルにシンプルな形式で書き込みます。

次に、サービスのログを読み取る小さなアプリケーションを作成し、既存のログと追加されたログの両方をライブビューとして表示します。

問題は、サービスが新しい行を追加するためにテキストファイルをロックすると同時に、ビューアアプリケーションがファイルを読み取りのためにロックすることです。

サービスコード:

void WriteInLog(string logFilePath, data)
{
    File.AppendAllText(logFilePath, 
                       string.Format("{0} : {1}\r\n", DateTime.Now, data));
}

ビューアコード:

int index = 0;
private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                using (StreamReader sr = new StreamReader(logFilePath))
                {
                    while (sr.Peek() >= 0)  // reading the old data
                    {
                        AddLineToGrid(sr.ReadLine());
                        index++;
                    }
                    sr.Close();
                }

                timer1.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }


private void timer1_Tick(object sender, EventArgs e)
        {
            using (StreamReader sr = new StreamReader(logFilePath))
            {
                // skipping the old data, it has read in the Form1_Load event handler
                for (int i = 0; i < index ; i++) 
                    sr.ReadLine();

                while (sr.Peek() >= 0) // reading the live data if exists
                {
                    string str = sr.ReadLine();
                    if (str != null)
                    {
                        AddLineToGrid(str);
                        index++;
                    }
                }
                sr.Close();
            }
        }

コードの読み書きに問題はありますか?

問題を解決するには?


回答:


120

サービスとリーダーの両方がログファイルを非排他的に開くことを確認する必要があります。これを試して:

サービス(例ではライター)のFileStream場合、次のように作成されたインスタンスを使用します。

var outStream = new FileStream(logfileName, FileMode.Open, 
                               FileAccess.Write, FileShare.ReadWrite);

読者のために同じを使用しますが、ファイルアクセスを変更します。

var inStream = new FileStream(logfileName, FileMode.Open, 
                              FileAccess.Read, FileShare.ReadWrite);

また、FileStreamインプリメントIDisposableは、どちらの場合もusing、たとえばライターの場合にステートメントの使用を検討することを確認します。

using(var outStream = ...)
{
   // using outStream here
   ...
}

幸運を!



完璧-FileAccess.Readを使用したFile.Open()で十分だと思っていましたが、それでは不十分です。しかし、これはそうです。:)
neminem

ライターは1人しかいないので、FileShare.Readで十分ではないでしょうか。
crokusek

2
また、FileStream実装することに注目する価値がありますIDisposable
Weeksdev

28

テキストファイルの読み取り中に、共有モードを明示的に設定します。

using (FileStream fs = new FileStream(logFilePath, 
                                      FileMode.Open, 
                                      FileAccess.Read,    
                                      FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (sr.Peek() >= 0) // reading the old data
        {
           AddLineToGrid(sr.ReadLine());
           index++;
        }
    }
}

2
StreamReader.Disposeが自動的にストリームを閉じるため、ストリームを明示的に閉じる必要はありません。
nothrow

2
ファイル共有は、読み取りだけでなく、読み取りストリームと書き込みストリームの両方で設定する必要があると思います。
LEO

StreamReader.Dispose は、誤っていない場合はFileStream 破棄します。ここでは、2回廃棄されます。
Eregrith

12
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

->これはファイルをロックしません。


1
データを読み取るときに機能するようです。ロックされたファイルを書き込むときにエラーが表示されます。
webzy 2016

8

問題は、ログに書き込んでいるときにファイルを排他的にロックしているため、StreamReaderでファイルを開くことがまったくできません。

読み取り専用モードでファイルを開こうとする必要があります。

using (FileStream fs = new FileStream("myLogFile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (!fs.EndOfStream)
        {
            string line = fs.ReadLine();
            // Your code here
        }
    }
}

今私が知っているように、File.ReadAllText()読み取り専用モードで失敗します。
bohdan_trotsenko 2013年

@modosansrevesええ、ファイルの読み取り専用の場合にスローされるとドキュメントに記載されているので、私は回答のその部分を削除しましたUnauthorizedAccessException-回答時にそれを見逃していたに違いありません!
ジェームズ

5

数年前に同じことをしたのを覚えています。いくつかのグーグルクエリの後、私はこれを見つけました:

    FileStream fs = new FileStream(@”c:\test.txt”, 
                                   FileMode.Open, 
                                   FileAccess.Read,        
                                   FileShare.ReadWrite);

つまり、FileStream()でFileShare.ReadWrite属性を使用します。

Balaji Rameshのブログにあります


1

ファイルをコピーしてから読み取ってみましたか?

大きな変更が行われたときはいつでもコピーを更新してください。


2
私はこれを試しましたが、それは最善の解決策ではありません。ファイルのコピーには時間がかかります。4MBのファイルの場合、20秒ほどかかります。
Seichi

-1

この方法は、テキストファイルをロックせずに最速で読み取るのに役立ちます。

private string ReadFileAndFetchStringInSingleLine(string file)
    {
        StringBuilder sb;
        try
        {
            sb = new StringBuilder();
            using (FileStream fs = File.Open(file, FileMode.Open))
            {
                using (BufferedStream bs = new BufferedStream(fs))
                {
                    using (StreamReader sr = new StreamReader(bs))
                    {
                        string str;
                        while ((str = sr.ReadLine()) != null)
                        {
                            sb.Append(str);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        catch (Exception ex)
        {
            return "";
        }
    }

この方法がお役に立てば幸いです。


1
私が理解している限り、このメソッドはファイル全体を文字列に読み取り、途中で例外を飲み込みます。それがファイルのロックにどのように役立つのかはわかりません。
デフォルトのロケール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.