.NET Framework 4.5以降で追加された「テキストファイルの内容を非同期で書き込むためのメソッド」の利用法を説明する。
テキストファイルの読み書きは、特にそのファイルサイズが大きいと時間がかかるものだ。その待ち時間中にUIが無応答になってしまうのを避けるには、読み書き処理を非同期的に行えばよい。.NET Framework 4.5からは、そのようなコードが簡単に書けるようになっている。本稿では、非同期的にテキストファイルへ書き込む方法を解説する。
特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。
テキストファイルの内容を非同期的に読み込む方法は、次のTIPSを参照してほしい。
テキストファイルに書き込むには、その目的や利用している.NET Frameworkのバージョンによって、さまざまな方法がある。適切な方法を選んでほしい。
なお、本稿に掲載したサンプルコードをそのまま試すにはVisual Studio 2015以降が必要である。サンプルコードはコンソールアプリの一部であり、コードの冒頭に以下の宣言が必要となる。また、サンプルということで、例外処理は省略している。実際には、指定したパスが存在していなかったり、アクセス権がなくて書き込めなかったりしたときなどに例外が発生するので、適切にtry〜catchしていただきたい。
using static System.Console;
Imports System.Console
テキストファイルを非同期的に読み込むときはStreamReaderクラス(System.IO名前空間)を使うが、非同期的に書き込むにはStreamWriterクラス(System.IO名前空間)を使う。
StreamWriterクラスには、非同期的に書き込むためのメソッドとして次の2つが用意されている。
WriteLineAsyncメソッドで追加される改行記号は、既定では「\r\n」(VBではvbCrLf)である。変更したい場合は、書き込む前にStreamWriterオブジェクトのNewLineプロパティに設定すればよい。
WriteAsyncメソッド/WriteLineAsyncメソッドで書き込んだ後、StreamWriterオブジェクトをクローズする(=明示的にCloseメソッドを呼び出すか、あるいは、usingブロックから抜ける)と、完全にファイルへ書き出される。クローズするまでは、ファイルに書き出されていないテキストがバッファリングされてメモリ上に残っている可能性があるので、注意してほしい。例えばログの書き出しなどのように、ファイルを開いたままで(=StreamWriterオブジェクトをクローズすることなく)しばらく書き込みを中断するときには、StreamWriterオブジェクトのFlushAsyncメソッドを呼び出すようにする。そうすれば、そこまでの内容が完全にファイルへ書き出される。
なお、複数のスレッドから同時に書き込む可能性がある場合には、スレッド間の排他ロックが必要になるが、非同期メソッドに対してはlockステートメントが使えない。代わりにSemaphoreSlimクラス(System.Threading名前空間)などを使う。詳しくは次のTIPSをご覧いただきたい。
StreamWriterクラスのインスタンスを作り、WriteAsyncメソッド/WriteLineAsyncメソッドで書き込む(次のコード)。指定したファイルがないときは、新しく作られる。既にファイルがあるときは、その内容が上書きされる。文字エンコーディングを指定しなければUTF-8になる(文字エンコーディングを指定する方法は後述)。
なお、上書きされるのは、「StreamWriterクラスのインスタンスを作るごと」にである。この例のように同じStreamWriterオブジェクトに対して、WriteAsyncメソッド/WriteLineAsyncメソッドを繰り返し呼び出しても上書きされず、どんどん追加されていく。
static async void OverWriteAsync()
{
// 書き込むテキストファイル(実行ファイルと同じフォルダに作られる)
const string TextFilePath = @".\sample_utf8.txt";
// StreamWriterオブジェクトを作る
using (var writer = new System.IO.StreamWriter(TextFilePath))
{
// 非同期的に文字列を書き込む
await writer.WriteLineAsync("業務アプリInsider");
await writer.WriteAsync("非同期的に書き込むには?");
} // usingを抜けるときにファイルがクローズされる
// 書き込んだ内容を読み取ってコンソールに出力する
using (var reader = new System.IO.StreamReader(TextFilePath))
{
string allLines = await reader.ReadToEndAsync();
WriteLine($"書き込んだファイルの内容:「{allLines}」");
}
}
Async Sub OverWriteAsync()
' 書き込むテキストファイル(実行ファイルと同じフォルダに作られる)
Const TextFilePath As String = ".\sample_utf8.txt"
' StreamWriterオブジェクトを作る
Using writer = New System.IO.StreamWriter(TextFilePath)
' 非同期的に文字列を書き込む
Await writer.WriteLineAsync("業務アプリInsider")
Await writer.WriteAsync("非同期的に書き込むには?")
End Using ' usingを抜けるときにファイルがクローズされる
' 書き込んだ内容を読み取ってコンソールに出力する
Using reader = New System.IO.StreamReader(TextFilePath)
Dim allLines As String = Await reader.ReadToEndAsync()
WriteLine($"書き込んだファイルの内容:「{allLines}」")
End Using
End Sub
上のメソッドをコンソールアプリのMainメソッド内から呼び出してみると、次のコードのようになる。
OverWriteAsync();
WriteLine("OverWriteAsyncメソッドの呼び出し完了");
// 出力例:
// OverWriteAsyncメソッドの呼び出し完了
// 書き込んだファイルの内容:「業務アプリInsider
// 非同期的に書き込むには?」
OverWriteAsync()
WriteLine("OverWriteAsyncメソッドの呼び出し完了")
' 出力例:
' OverWriteAsyncメソッドの呼び出し完了
' 書き込んだファイルの内容:「業務アプリInsider
' 非同期的に書き込むには?」
StreamWriterクラスのインスタンスを作るとき、第2引数にtrueを指定する。後は、上書きするときと同様に、WriteAsyncメソッド/WriteLineAsyncメソッドで書き込む(次のコード)。指定したファイルがないときは、新しく作られる。既にファイルがあるときは、その後ろに書き込んだ内容が追加される。また、この例では、文字エンコーディングとしてシフトJISを指定している。
static async void AppendWriteAsync()
{
// 書き込むテキストファイル(実行ファイルと同じフォルダに作られる)
const string TextFilePath = @".\sample_sjis.txt";
// シフトJISの文字エンコーディングオブジェクトを用意する
var sjis = System.Text.Encoding.GetEncoding("shift_jis");
// StreamWriterオブジェクトを作る
using (var writer = new System.IO.StreamWriter(TextFilePath, true, sjis))
{
// 非同期的に文字列を書き込む
await writer.WriteAsync($"{System.DateTime.Now:mm:ss.fff} ");
} // usingを抜けるときにファイルがクローズされる
// 書き込んだ内容を読み取ってコンソールに出力する
using (var reader = new System.IO.StreamReader(TextFilePath, sjis))
{
string allLines = await reader.ReadToEndAsync();
WriteLine($"書き込んだファイルの内容:「{allLines}」");
}
}
Async Sub AppendWriteAsync()
' 書き込むテキストファイル(実行ファイルと同じフォルダに作られる)
Const TextFilePath As String = ".\sample_sjis.txt"
' シフトJISの文字エンコーディングオブジェクトを用意する
Dim sjis = System.Text.Encoding.GetEncoding("shift_jis")
' StreamWriterオブジェクトを作る
Using writer = New System.IO.StreamWriter(TextFilePath, True, sjis)
' 非同期的に文字列を書き込む
Await writer.WriteAsync($"{System.DateTime.Now:mm:ss.fff} ")
End Using ' usingを抜けるときにファイルがクローズされる
' 書き込んだ内容を読み取ってコンソールに出力する
Using reader = New System.IO.StreamReader(TextFilePath, sjis)
Dim allLines As String = Await reader.ReadToEndAsync()
WriteLine($"書き込んだファイルの内容:「{allLines}」")
End Using
End Sub
上のメソッドをコンソールアプリのMainメソッド内から呼び出してみると、次のコードのようになる。
AppendWriteAsync();
WriteLine("AppendWriteAsyncメソッドの呼び出し完了");
// 出力例(1回目):
// AppendWriteAsyncメソッドの呼び出し完了
// 書き込んだファイルの内容:「59:22.424」
// 出力例(2回目):
// AppendWriteAsyncメソッドの呼び出し完了
// 書き込んだファイルの内容:「59:22.424 59:23.439」
AppendWriteAsync()
WriteLine("AppendWriteAsyncメソッドの呼び出し完了")
' 出力例(1回目):
' AppendWriteAsyncメソッドの呼び出し完了
' 書き込んだファイルの内容:「59:22.424」
' 出力例(2回目):
' AppendWriteAsyncメソッドの呼び出し完了
' 書き込んだファイルの内容:「59:22.424 59:23.439」
テキストファイルへ非同期的に書き込むには、StreamWriterクラスを使う。StreamWriterオブジェクトを作るときに、上書き/追加の区別(既定は上書き)、あるいは、文字エンコーディングを指定できる。
利用可能バージョン:.NET Framework 4.5以降
カテゴリ:クラスライブラリ 処理対象:テキストファイル
使用ライブラリ:StreamWriterクラス(System.IO名前空間)
使用ライブラリ:Encodingクラス(System.Text名前空間)
関連TIPS:テキストファイルの内容を非同期的に読み込むには?[C#/VB、.NET 4.5]
関連TIPS:ファイルにテキストを書き込むには?[C#、VB]
関連TIPS:テキスト・ファイルの内容を簡単に書き込むには?[2.0のみ、C#、VB]
関連TIPS:オープン中のファイルにアクセスするには?[C#、VB]
関連TIPS:async/awaitで例外処理をするには?[C#/VB]
関連TIPS:非同期:awaitを含むコードをロックするには?(SemaphoreSlim編)[C#、VB]
関連TIPS:非同期:awaitを含むコードをロックするには?(AsyncLock編)[C#、VB]
関連TIPS:ファイルをコピー/削除/リネーム/移動するには?
関連TIPS:バイナリ・ファイルを読み書きするには?
関連TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]
関連TIPS:VB.NETでクラス名を省略してメソッドや定数を利用するには?
関連TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]
Copyright© Digital Advantage Corp. All Rights Reserved.