スポンサーリンク

2013年7月24日水曜日

StreamReader と StreamWriter

StreamReader と StreamWriter の基本的動作で気になったことがあったのでさくっと調べてみた。

僕は基本的に FileStream などの Stream を継承したクラスのオブジェクトを渡してインスタンス化するのが好き。
で、もしかしたら読み込んだデータを加工してそのまま同じストリームに書き込みたい、ということがあるかもしれない。

まあ、ないよね、普通。


じゃあ、FileStream オブジェクトを共有して StreamReader と StreamWriter を使えばいんじゃないかということで何も考えずに書いてみる。

using (FileStream stream = new FileStream("Test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    string content = string.Empty;

    using (StreamReader reader = new StreamReader(stream, Encoding.Default))
    {
         content = reader.ReadToEnd();
    }

    Console.WriteLine(content);

    using (StreamWriter writer = new StreamWriter(stream, Encoding.Default))
    {
         writer.Write(content);
         writer.WriteLine(string.Format("{0}: 書き込み!", DateTime.Now.ToLongTimeString()));
    }
}

これだと StreamWriter をインスタンス化するところで例外が発生する。

StreamReader が(StreamWriter も)Dispose メソッド内で元となっている Stream を閉じているから。
StreamWriter はついでにいうと Flush メソッドも呼び出している。

これが意外と落とし穴で、この Flush メソッドを呼び出さないとファイルに書き込みが行われない。当たり前っちゃ当たり前だけど、using ブロックを使ってたりすると、そのブロックを抜けるときに暗黙的に Close メソッドが呼び出され、その中でさらに Flush メソッドが呼び出されるから特に意識したりしないんだよね。

なので、using ブロックを使うと抜けるときにストリーム閉じるし自前でやるか!と以下のように書くとファイルの書き込みが行われない。

using (FileStream stream = new FileStream("Test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    string content = string.Empty;
    StreamReader reader = new StreamReader(stream, Encoding.Default);

    content = reader.ReadToEnd();

    Console.WriteLine(content);
    StreamWriter writer = new StreamWriter(stream, Encoding.Default);

    writer.Write(content);
    writer.WriteLine(string.Format("{0}: 書き込み!", DateTime.Now.ToLongTimeString()));
}

StreamReader と StreamWriter は閉じてないけど、そのベースとなっている FileStream は using ブロックを抜けるときに閉じられるのでファイルがロックされたままになるということはない。

けど、このままだとファイルの書き込みは行われない。先にも書いたように Flush メソッドが呼び出されてないから。

StreamWriter の Flush メソッドを呼び出してもいいし Close メソッドを呼び出してもいい。
けど、StreamWriter の Close メソッドを呼び出したら StreamReader の Close メソッドも呼び出さないと気持ち悪い。
とはいえ、Close メソッドを呼び出すとベースになる Stream も閉じられるので、もう一方の Close メソッドで例外が発生する(笑)。

そういうわけで、Flush メソッドを呼び出すのがいいのかな。こういう要件があるかどうかは置いておいて。

using (FileStream stream = new FileStream("Test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    string content = string.Empty;
    StreamReader reader = new StreamReader(stream, Encoding.Default);

    content = reader.ReadToEnd();

    Console.WriteLine(content);
    StreamWriter writer = new StreamWriter(stream, Encoding.Default);

    writer.Write(content);
    writer.WriteLine(string.Format("{0}: 書き込み!", DateTime.Now.ToLongTimeString()));
    writer.Flush();
}

0 件のコメント:

コメントを投稿