System.IO.File.ReadLines()
で IIS のログファイルを読み取るコードを書いていたのですが、本番環境に適用してみると
別のプロセスで使用されているため、プロセスはファイル “ほげほげ” にアクセスできません
などと怒られてしまいました。
結論
結論から言うと下記のようなユーティリティメソッドを作って利用するのが手っ取り早いです。
この内容については後述します。例外処理等は適当なので、必要に応じて追加してください。
下記のような感じで使えます。
var lines = FileText.ReadLines(path)
.Skip(2) // 2 行スキップ
.Where(x => x.Contains("HOGE")) // HOGE を含んでいれば
.Select(x => ConvertLine(x)); // なんか処理
foreach (var line in lines)
{
Console.WriteLine(line);
}
原因
IIS はログファイルを書き込む際に当然ながらファイルロックをかけていますが、どうやら System.IO.File.ReadLines()
は書き込みモードで開かれているファイルを読み込めないようです。
Microsoft の .NET Framework のソースコードを参照すると ReadLines
は下記のように実装されています。
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static IEnumerable<String> ReadLines(String path)
{
if (path == null)
throw new ArgumentNullException("path");
if (path.Length == 0)
throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), "path");
Contract.EndContractBlock();
return ReadLinesIterator.CreateIterator(path, Encoding.UTF8);
}
ReadLinesIterator.CreateIterator
で作られたイテレーターが返されています。これをさらにたどってみると…
internal class ReadLinesIterator : Iterator<string>
{
// ~中略~
private static ReadLinesIterator CreateIterator(string path, Encoding encoding, StreamReader reader)
{
return new ReadLinesIterator(path, encoding, reader ?? new StreamReader(path, encoding));
}
}
StreamReader
が引数なしで作成されることがわかります。さらに StreamReader
のコンストラクターを参照すると…
Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
FileShare.Read
となっています。この FileShare
という列挙体は非常にわかりにくいのですが、すでにロックされているファイルを開くとき、下記のように動作します。
Read
: 読み取りロックがかかっているファイルが開けるReadWrite
: 読み取り書き込みロックがかかっているファイルが開けるNone
: ロックされていないファイルのみ開ける
これが Read
になっているので書き込みロックのかかっているファイルが開けない、ということですね。
詳細
内容的には FileShare
を渡せるようにしただけです。
メソッドは IEnumerable<string>
を返すようにし、内部では yield return
で一行ごとに返していきます。
このときファイルの終端 (EOF) をチェックするのに Peek()
メソッドの戻り値をチェックしている例が多いですが、 EndOfStream
プロパティをチェックするか、実装例のように ReadLine()
の戻り値が null
かどうかで確認するようにします。