StreamReaderクラスおよびStreamWriterクラスはStreamに対してテキストの読み書きを行うためのクラスです。 Stream単体ではバイナリレベルでの読み書きしか行えませんが、StreamとStreamReader・StreamWriterを組み合わせて使うことでテキストレベルの読み書きが可能になり、ファイルに対して1行ずつ読み書きするといったことができるようになります。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtを書き込み用に開く
using (Stream stream = File.OpenWrite("sample.txt")) {
// streamに書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream)) {
// 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!");
}
}
// ファイルsample.txtを読み込み用に開く
using (Stream stream = File.OpenRead("sample.txt")) {
// streamから読み込むためのStreamReaderを作成
using (StreamReader reader = new StreamReader(stream)) {
// 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine());
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtを書き込み用に開く
Using stream As Stream = File.OpenWrite("sample.txt")
' streamに書き込むためのStreamWriterを作成
Using writer As New StreamWriter(stream)
' 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!")
End Using
End Using
' ファイルsample.txtを読み込み用に開く
Using stream As Stream = File.OpenRead("sample.txt")
' streamから読み込むためのStreamReaderを作成
Using reader As New StreamReader(stream)
' 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine())
End Using
End Using
End Sub
End Class
なお、StreamReader・StreamWriterはTextReader・TextWriterから派生したクラスです。 同じくTextReader・TextWriterから派生したクラスにStringReader・StringWriterが存在します。 こちらはStreamのかわりにString・StringBuilderに対してテキストの読み書きを行うためのクラスです。
StreamReaderのメソッドおよびStreamWriterのメソッドはどれもTextReader・TextWriterを継承したもので、そのほとんどはStringReader・StringWriterでも同じように動作します。
導入
StreamReader・StreamWriterの具体的な使い方に入る前に、StreamReader・StreamWriterを扱う上で念頭に置いておくべきことについて解説します。
StreamReader・StreamWriterとエンコーディング
StreamReader・StreamWriterでは、Encodingクラスを使った文字列のデコード・エンコードを行う機能が組み込まれています。 コンストラクタに目的のEncodingインスタンスを指定するだけで、UTF-8を始め、Shift_JISやEUC-JPなどの文字コードを深く意識すること無く簡単に扱うことができます。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
using (Stream stream = File.OpenWrite("sample-utf8.txt")) {
// UTF-8でエンコードして書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)) {
// 文字列はUTF-8にエンコードされた上でstreamに書き込まれる
writer.WriteLine("こんにちは世界");
}
}
using (Stream stream = File.OpenWrite("sample-shiftjis.txt")) {
// Shift_JISでエンコードして書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream, Encoding.GetEncoding("Shift_JIS"))) {
// 文字列はShift_JISにエンコードされた上でstreamに書き込まれる
writer.WriteLine("こんにちは世界");
}
}
using (Stream stream = File.OpenRead("sample-utf8.txt")) {
// UTF-8でデコードして読み込むためのStreamReaderを作成
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) {
// streamから読み込んだ文字列はUTF-8でデコードされて返される
Console.WriteLine(reader.ReadLine());
}
}
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
Using stream As Stream = File.OpenWrite("sample-utf8.txt")
' UTF-8でエンコードして書き込むためのStreamWriterを作成
Using writer As New StreamWriter(stream, Encoding.UTF8)
' 文字列はUTF-8にエンコードされた上でstreamに書き込まれる
writer.WriteLine("こんにちは世界")
End Using
End Using
Using stream As Stream = File.OpenWrite("sample-shiftjis.txt")
' Shift_JISでエンコードして書き込むためのStreamWriterを作成
Using writer As New StreamWriter(stream, Encoding.GetEncoding("Shift_JIS"))
' 文字列はShift_JISにエンコードされた上でstreamに書き込まれる
writer.WriteLine("こんにちは世界")
End Using
End Using
Using stream As Stream = File.OpenRead("sample-utf8.txt")
' UTF-8でデコードして読み込むためのStreamReaderを作成
Using reader As New StreamReader(stream, Encoding.UTF8)
' streamから読み込んだ文字列はUTF-8でデコードされて返される
Console.WriteLine(reader.ReadLine())
End Using
End Using
End Sub
End Class
Shift_JIS以外にも、Encoding.GetEncodingメソッドにコードページ番号やエンコーディング名を指定することによって、目的の文字コードに対応したEncodingを取得することができます。 具体的な取得方法や、取得できるEncodingについてはファイル入出力 §.Encodingの取得を参照してください。
Encodingを指定しなかった場合には、デフォルトでUTF-8が使用されます。 実行環境のデフォルトのエンコーディングではないので注意してください。 Streamから読み込もうとしているデータのエンコーディングと、StreamReaderに指定するエンコーディングが異なる場合(たとえばShift_JISのファイルを読もうとしてUTF-8を指定したStreamReaderを使った場合)は、当然文字化けが起こるので注意してください。
またStreamWriterでは、Encodingの指定の仕方によってはバイト順マーク(BOM)が書き込まれます。 BOMの扱いについてはEncodingクラスとBOMありなしの制御で詳しく解説していますので必要に応じて参照してください。 StreamReaderでのBOMの扱いについては、こちらで解説しています。
読み込み・書き込み対象とStreamReader・StreamWriterの作成方法
任意のStreamからの読み込み・書き込み
StreamReader・StreamWriterでは読み込み・書き込み対象となるStreamを指定してインスタンスを作成します。 ファイルであればFileStream、バイト配列(メモリ上の領域)であればMemoryStreamなど、データソースとなるStreamを読み込み・書き込み対象として指定します。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtを書き込み用に開く
using (FileStream stream = File.OpenWrite("sample.txt")) {
// streamに書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream)) {
// 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!");
}
}
// ファイルsample.txtを読み込み用に開く
using (FileStream stream = File.OpenRead("sample.txt")) {
// streamから読み込むためのStreamReaderを作成
using (StreamReader reader = new StreamReader(stream)) {
// 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine());
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtを書き込み用に開く
Using stream As FileStream = File.OpenWrite("sample.txt")
' streamに書き込むためのStreamWriterを作成
Using writer As New StreamWriter(stream)
' 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!")
End Using
End Using
' ファイルsample.txtを読み込み用に開く
Using stream As FileStream = File.OpenRead("sample.txt")
' streamから読み込むためのStreamReaderを作成
Using reader As New StreamReader(stream)
' 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine())
End Using
End Using
End Sub
End Class
using System;
using System.IO;
class Sample {
static void Main()
{
byte[] data = new byte[32];
// 既存のバイト配列に書き込むMemoryStreamを作成
using (MemoryStream stream = new MemoryStream(data, true)) {
// streamに書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream)) {
// 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!");
}
}
// 既存のバイト配列から読み込むMemoryStreamを作成
using (MemoryStream stream = new MemoryStream(data, false)) {
// streamから読み込むためのStreamReaderを作成
using (StreamReader reader = new StreamReader(stream)) {
// 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine());
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Dim data(31) As Byte
' 既存のバイト配列に書き込むMemoryStreamを作成
Using stream As New MemoryStream(data, True)
' streamに書き込むためのStreamWriterを作成
Using writer As New StreamWriter(stream)
' 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!")
End Using
End Using
' 既存のバイト配列から読み込むMemoryStreamを作成
Using stream As New MemoryStream(data, False)
' streamから読み込むためのStreamReaderを作成
Using reader As New StreamReader(stream)
' 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine())
End Using
End Using
End Sub
End Class
読み込み・書き込み対象がファイルの場合は、直接ファイル名を指定してStreamReader・StreamWriterインスタンスを作成することもできます。
ファイルからの読み込み・書き込み
ファイルからの読み書きを行う場合、StreamReader・StreamWriterに直接ファイル名を指定することによりFileStreamインスタンスの作成を省略することが出来ます。 StreamReader・StreamWriterのコンストラクタでファイル名を指定した場合、自動的にFileStreamの作成が行われ、指定されたファイルに対して読み書きを行うStreamReader・StreamWriterを作成することができます。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtに書き込むためのStreamWriterを作成
using (StreamWriter writer = new StreamWriter("sample.txt")) {
// 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!");
}
// ファイルsample.txtから読み込むためのStreamReaderを作成
using (StreamReader reader = new StreamReader("sample.txt")) {
// 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine());
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtに書き込むためのStreamWriterを作成
Using writer As New StreamWriter("sample.txt")
' 文字列を改行付きで書き込む
writer.WriteLine("Hello, world!")
End Using
' ファイルsample.txtから読み込むためのStreamReaderを作成
Using reader As New StreamReader("sample.txt")
' 文字列を一行読み込んで表示する
Console.WriteLine(reader.ReadLine())
End Using
End Sub
End Class
後述するFileクラスのOpenText等のメソッドを使うことでもStreamReader・StreamWriterのインスタンスを作成することができます。
StreamReader
ここではStreamReaderの使い方について解説します。 このセクションで紹介するサンプルコードでは、簡単化のためファイル名を指定してStreamReaderを作成していますが、FileStreamやMemoryStreamなどのStreamを指定してStreamReaderのインスタンスを生成した場合も同じように動作します。 また、エンコーディングの指定も省略しています。 サンプル中で使用されるファイルsample.txtは、UTF-8でエンコードされているテキストファイルを想定しています。
1行ずつの読み込み (ReadLine)
ReadLineメソッドは、ストリームから1行分の文字列を読み込みます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// 一行分読み込む
string line = reader.ReadLine();
Console.WriteLine("line 1 : {0}", line);
// 次の行を読み込む
line = reader.ReadLine();
Console.WriteLine("line 2 : {0}", line);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' 一行分読み込む
Dim line As String = reader.ReadLine()
Console.WriteLine("line 1 : {0}", line)
' 次の行を読み込む
line = reader.ReadLine()
Console.WriteLine("line 2 : {0}", line)
End Using
End Sub
End Class
EndOfStreamプロパティを参照すると、ストリームの末尾まで読み込んだかどうかを調べることができます。 これらを組み合わせてストリームの末尾までを一行ずつ読み込むには次のようにします。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// 行番号
int lineNumber = 1;
// 無限ループ
for (;;) {
if (reader.EndOfStream)
// ストリームの末尾(EOF)に達したので読み込みを終了する
break;
// 一行分読み込む
string line = reader.ReadLine();
// 読み込んだ行に行番号を付けて表示する
Console.WriteLine("{0}: {1}", lineNumber, line);
lineNumber++;
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' 行番号
Dim lineNumber As Integer = 1
' 無限ループ
Do
If reader.EndOfStream Then
' ストリームの末尾(EOF)に達したので読み込みを終了する
Exit Do
End If
' 一行分読み込む
Dim line As String = reader.ReadLine()
' 読み込んだ行に行番号を付けて表示する
Console.WriteLine("{0}: {1}", lineNumber, line)
lineNumber += 1
Loop
End Using
End Sub
End Class
ReadLineメソッドでは、ストリームの末尾に到達している場合にはnull/Nothingが返されます。 末尾以外の空行(改行文字のみの行)の場合は、null/Nothingではなく長さ0の文字列(String.Empty)が返されます。 ReadLineメソッドの戻り値がnull/Nothingかどうかを調べることでもEOFの判定が行えます。 そのため、上記の例は次のようにも書き換えられます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// 行番号
int lineNumber = 1;
// 無限ループ
for (;;) {
// 一行分読み込む
string line = reader.ReadLine();
if (line == null)
// ストリームの末尾(EOF)に達したので読み込みを終了する
break;
// 読み込んだ行に行番号を付けて表示する
Console.WriteLine("{0}: {1}", lineNumber, line);
lineNumber++;
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' 行番号
Dim lineNumber As Integer = 1
' 無限ループ
Do
' 一行分読み込む
Dim line As String = reader.ReadLine()
If line Is Nothing Then
' ストリームの末尾(EOF)に達したので読み込みを終了する
Exit Do
End If
' 読み込んだ行に行番号を付けて表示する
Console.WriteLine("{0}: {1}", lineNumber, line)
lineNumber += 1
Loop
End Using
End Sub
End Class
ReadLineメソッドでは、CR+LF、CR(キャリッジリターン)、LF(ラインフィード)の各改行コードまでを1行として扱います。 StreamReaderでは、どの改行コードを行区切りとして扱うかを指定することは出来ません(StreamWriterではStreamWriter.NewLineプロパティで変更可能です)。
また、ReadLineメソッドの戻り値には改行文字は含まれません。 したがって戻り値に対してchomp
/chop
などの操作を行う必要はありませんが、一方、改行文字を維持して1行ずつ読み込むことはできません。
参考までに、chomp
相当の操作を行うにはString.TrimEndメソッドが使えます。
File.ReadAllLines, ReadLinesメソッドを使った読み込み
読み込み元がファイルに限定される場合は、行ごとの読み込みにFileクラスのメソッドを使うこともできます。 File.ReadAllLinesメソッドを使うと、ファイルを読み込み行ごとに分割したものを文字列配列として取得できます。 次の例では、File.ReadAllLinesでファイルを読み込み行番号を付けて表示しています。 比較のためにStreamReader.ReadLineを使って同等の処理を行う例も併記しています。
using System;
using System.Collections.Generic;
using System.IO;
class Sample {
static void Main()
{
// File.ReadAllLinesでファイル全体を読み込んだ後、行番号を付けて表示する
string[] lineArray = File.ReadAllLines("sample.txt");
for (int index = 0; index < lineArray.Length; index++) {
Console.WriteLine("{0}: {1}", index + 1, lineArray[index]);
}
// StreamReaderでファイル全体を読み込んだ後、行番号を付けて表示する
List<string> lineList = new List<string>();
using (StreamReader reader = new StreamReader("sample.txt")) {
for (string line = reader.ReadLine(); line != null; line = reader.ReadLine()) {
lineList.Add(line);
}
}
for (int index = 0; index < lineList.Count; index++) {
Console.WriteLine("{0}: {1}", index + 1, lineList[index]);
}
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Class Sample
Shared Sub Main()
' File.ReadAllLinesでファイル全体を読み込んだ後、行番号を付けて表示する
Dim lineArray() As String = File.ReadAllLines("sample.txt")
For index As Integer = 0 To lineArray.Length - 1
Console.WriteLine("{0}: {1}", index + 1, lineArray(index))
Next
' StreamReaderでファイル全体を読み込んだ後、行番号を付けて表示する
Dim lineList As New List(Of String)()
Using reader As New StreamReader("sample.txt")
Dim line As String = reader.ReadLine()
Do Until line Is Nothing
lineList.Add(line)
line = reader.ReadLine()
Loop
End Using
For index As Integer = 0 To lineList.Count - 1
Console.WriteLine("{0}: {1}", index + 1, lineList(index))
Next
End Sub
End Class
.NET Framework 4からはFile.ReadLinesメソッドも使うことができるようになっています。 File.ReadAllLinesはファイル全体の読み込みが終わるまで結果が返されないのに対し、File.ReadLinesでは一行読み込むごとに結果が返されます。 巨大なファイルを読み込む場合や、ファイルの先頭部分のみを読み込みたい場合などではReadAllLinesメソッドを使うよりもReadLinesメソッドを使った方が効率的です。
using System;
using System.Collections.Generic;
using System.IO;
class Sample {
static void Main()
{
// File.ReadLinesでファイルを1行ずつ読み込み、行番号を付けて表示する
int lineNumber = 1;
foreach (string line in File.ReadLines("sample.txt")) {
Console.WriteLine("{0}: {1}", lineNumber, line);
lineNumber++;
}
// StreamReaderでファイルを1行ずつ読み込み、行番号を付けて表示する
lineNumber = 1;
using (StreamReader reader = new StreamReader("sample.txt")) {
foreach (string line in ReadLines(reader)) {
Console.WriteLine("{0}: {1}", lineNumber, line);
lineNumber++;
}
}
}
// File.ReadLinesに相当する処理をStreamReaderに対して行えるようにするメソッド
private static IEnumerable<string> ReadLines(StreamReader reader)
{
for (;;) {
string line = reader.ReadLine();
if (line == null)
yield break;
else
yield return line;
}
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Class Sample
Shared Sub Main()
' File.ReadLinesでファイルを1行ずつ読み込み、行番号を付けて表示する
Dim lineNumber As Integer = 1
For Each line As String In File.ReadLines("sample.txt")
Console.WriteLine("{0}: {1}", lineNumber, line)
lineNumber += 1
Next
' StreamReaderでファイルを1行ずつ読み込み、行番号を付けて表示する
lineNumber = 1
Using reader As New StreamReader("sample.txt")
For Each line As String In ReadLines(reader)
Console.WriteLine("{0}: {1}", lineNumber, line)
lineNumber += 1
Next
End Using
End Sub
' File.ReadLinesに相当する処理をStreamReaderに対して行えるようにするメソッド
Private Shared Iterator Function ReadLines(ByVal reader As StreamReader) As IEnumerable(Of String)
Do
Dim line As String = reader.ReadLine()
If line Is Nothing Then
Exit Function
Else
Yield line
End If
Loop
End Function
End Class
この例で使用しているイテレータ構文についてはイテレータを参照してください。
Fileクラスのメソッドを使ったファイルの読み込みについてはファイル入出力を参照してください。
全テキストの読み込み (ReadToEnd)
ReadToEndメソッドは、ストリームの現在位置から末尾までをひとつながりの文字列として読み込みます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// ストリームの末尾まですべてを読み込む
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' ストリームの末尾まですべてを読み込む
Dim content As String = reader.ReadToEnd()
Console.WriteLine(content)
End Using
End Sub
End Class
File.ReadAllTextを使った読み込み
読み込み元がファイルに限定される場合はFileクラスのメソッドを使うこともできます。 File.ReadAllTextメソッドを使うと、StreamReaderを使わずにファイルの内容を文字列として読み込むことができます。
using System;
using System.IO;
class Sample {
static void Main()
{
// File.ReadAllTextでファイル全体を読み込む
string content = File.ReadAllText("sample.txt");
Console.WriteLine(content);
// StreamReader.ReadToEndでファイル全体を読み込む
using (StreamReader reader = new StreamReader("sample.txt")) {
content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' File.ReadAllTextでファイル全体を読み込む
Dim content As String = File.ReadAllText("sample.txt")
Console.WriteLine(content)
' StreamReader.ReadToEndでファイル全体を読み込む
Using reader As New StreamReader("sample.txt")
content = reader.ReadToEnd()
Console.WriteLine(content)
End Using
End Sub
End Class
Fileクラスのメソッドを使ったファイルの読み込みについてはファイル入出力を参照してください。
文字単位での読み込み (Read, Peek)
Readメソッドは、ストリームから1文字ずつ読み込みます。 Readメソッドで読み込まれるのは1バイトずつではありません。 戻り値はint/Integerで、ストリームの末尾に達した場合は-1が返されます。 それ以外の場合は読み込めた文字(char)の値となるため、戻り値をchar/Charにキャストして使います。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// 無限ループ
for (;;) {
// 一文字読み込む
int ch = reader.Read();
if (ch == -1)
// ストリームの末尾(EOF)に達したので読み込みを終了する
break;
// 読み込んだ値をcharにキャストして表示する
char c = (char)ch;
Console.Write(c);
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' 無限ループ
Do
' 一文字読み込む
Dim ch As Integer = reader.Read()
If ch = -1 Then
' ストリームの末尾(EOF)に達したので読み込みを終了する
Exit Do
End If
' 読み込んだ値をCharに変換して表示する
Dim c As Char = ChrW(ch)
Console.Write(c)
Loop
End Using
End Sub
End Class
Peekメソッドを使うと、次の1文字を先読みすることができます。 Peekメソッドの戻り値はReadメソッドの戻り値と同様です。
次の例では、ReadメソッドとPeekメソッドを使って読み込みを行い、各行に行番号を付けて表示しています。 この例ではCR, LF, CR+LFを改行として扱い、それらの改行文字は変換せずそのまま表示しています。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
int lineNumber = 1;
Console.Write("{0}: ", lineNumber);
// 無限ループ
for (;;) {
// 一文字読み込む
int ch = reader.Read();
if (ch == -1)
// ストリームの末尾(EOF)に達したので読み込みを終了する
break;
// 値をcharにキャストして表示
char c = (char)ch;
Console.Write(c);
// 読み込んだ文字がCRだった場合
if (c == '\r') {
// 次の一文字を読み込む
if (reader.Peek() == (int)'\n')
// 現在のCRにLFが後続するので、ここでは何もせず処理を続ける
// (次のループでLFが読まれる)
continue;
else
// CR単独の改行なので、行番号を表示する
Console.Write("{0}: ", ++lineNumber);
}
// 読み込んだ文字がLFだった場合
else if (c == '\n') {
// LF単独もしくはCR+LFの改行なので、行番号を表示する
Console.Write("{0}: ", ++lineNumber);
}
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
Dim lineNumber As Integer = 1
Console.Write("{0}: ", lineNumber)
' 無限ループ
Do
' 一文字読み込む
Dim ch As Integer = reader.Read()
If ch = -1 Then
' ストリームの末尾(EOF)に達したので読み込みを終了する
Exit Do
End If
' 値をCharに変換して表示
Dim c As Char = ChrW(ch)
Console.Write(c)
' 読み込んだ文字がCRだった場合
If ch = AscW(vbCr) Then
' 次の一文字を読み込む
If reader.Peek() = AscW(vbLf)
' 現在のCRにLFが後続するので、ここでは何もせず処理を続ける
' (次のループでLFが読まれる)
Else
' CR単独の改行なので、行番号を表示する
lineNumber += 1
Console.Write("{0}: ", lineNumber)
End If
' 読み込んだ文字がLFだった場合
Else If ch = AscW(vbLf)
' LF単独もしくはCR+LFの改行なので、行番号を表示する
lineNumber += 1
Console.Write("{0}: ", lineNumber)
End If
Loop
End Using
End Sub
End Class
引数を指定せずにReadメソッドを呼び出した場合は1文字ずつの読み込みが行われますが、Readメソッドでは読み込み先のバッファと文字数を指定して読み込むこともできるようになっています。 文字を1文字ずつチェックする必要がある場合などでは、Readメソッドで1文字ずつ読み込んでチェックするよりも、バッファに複数文字読み込んでチェックした方がメソッド呼び出しの回数が少なくなり効率的です。
次の例では、Readメソッドで一旦バッファに読み込んでから1文字ずつチェックして全角英字を半角英字に置き換えています。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamReader reader = new StreamReader("sample.txt")) {
// 読み込んだ文字を格納しておくためのバッファ (32文字分)
char[] buffer = new char[32];
for (;;) {
// StreamReaderから読み込んでバッファに格納する
int len = reader.Read(buffer, 0, buffer.Length);
if (len == 0)
// ストリームの末尾(EOF)に達したので読み込みを終了する
break;
// 読み込んだ文字を1文字ずつ検査して全角英字を半角英字に変換する
for (int i = 0; i < len; i++) {
if ('A' <= buffer[i] && buffer[i] <= 'Z')
buffer[i] = (char)((int)buffer[i] - (int)'A' + (int)'A');
else if ('a' <= buffer[i] && buffer[i] <= 'z')
buffer[i] = (char)((int)buffer[i] - (int)'a' + (int)'a');
}
// bufferを文字列に変換して表示
Console.Write(new string(buffer, 0, len));
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using reader As New StreamReader("sample.txt")
' 読み込んだ文字を格納しておくためのバッファ (32文字分)
Dim buffer(31) As Char
Do
' StreamReaderから読み込んでバッファに格納する
Dim len As Integer = reader.Read(buffer, 0, buffer.Length)
If len = 0 Then
' ストリームの末尾(EOF)に達したので読み込みを終了する
Exit Do
End If
' 読み込んだ文字を1文字ずつ検査して全角英字を半角英字に変換する
For i As Integer = 0 To len - 1
If "A"c <= buffer(i) AndAlso buffer(i) <= "Z"c Then
buffer(i) = ChrW(AscW(buffer(i)) - AscW("A") + AscW("A"))
Else If "a"c <= buffer(i) AndAlso buffer(i) <= "z"c Then
buffer(i) = ChrW(AscW(buffer(i)) - AscW("a") + AscW("a"))
End If
Next
' bufferを文字列に変換して表示
Console.Write(New String(buffer, 0, len))
Loop
End Using
End Sub
End Class
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 0123456789 あいうアイウ漢字
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 0123456789 あいうアイウ漢字
ブロッキングを行う読み込み (ReadBlock)
ReadBlockメソッドは、読み込み先のバッファと文字数を指定してReadメソッドを呼び出した場合と基本的には同じです。
using System;
using System.IO;
class Sample {
static void Main()
{
char[] buffer = new char[32];
using (StreamReader reader = new StreamReader("sample.txt")) {
// Readメソッドを使ってストリームの末尾に達するまで読み込みを行い、内容を表示する
for (;;) {
int len = reader.Read(buffer, 0, buffer.Length);
if (len == 0)
break;
Console.Write(new string(buffer, 0, len));
}
}
using (StreamReader reader = new StreamReader("sample.txt")) {
// ReadBlockメソッドを使ってストリームの末尾に達するまで読み込みを行い、内容を表示する
for (;;) {
int len = reader.ReadBlock(buffer, 0, buffer.Length);
if (len == 0)
break;
Console.Write(new string(buffer, 0, len));
}
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Dim buffer(31) As Char
Using reader As New StreamReader("sample.txt")
' Readメソッドを使ってストリームの末尾に達するまで読み込みを行い、内容を表示する
Do
Dim len As Integer = reader.Read(buffer, 0, buffer.Length)
If len = 0 Then Exit Do
Console.Write(New String(buffer, 0, len))
Loop
End Using
Using reader As New StreamReader("sample.txt")
' ReadBlockメソッドを使ってストリームの末尾に達するまで読み込みを行い、内容を表示する
Do
Dim len As Integer = reader.ReadBlock(buffer, 0, buffer.Length)
If len = 0 Then Exit Do
Console.Write(New String(buffer, 0, len))
Loop
End Using
End Sub
End Class
ReadメソッドとReadBlockメソッドの違いは、読み込みに際してブロッキングが行われるかどうかという点にあります。 Readメソッドではブロッキングされませんが、ReadBlockメソッドではブロッキングされます。
ReadBlockメソッドは、ストリームの末尾に到達するか、指定された文字数の読み込みが終わるまで処理を返しません。 一方Readメソッドは、ストリームの末尾に到達した場合のほか、指定された文字数よりも少ない文字数しか読み込めなかった場合でも処理を返します。 これはNetworkStreamを使った読み込み中にデータの途中までを受信した場合などに起こる可能性があります(FileStreamやMemoryStreamでは通常起こらないと思われます)。 従って、データの途中でも読み込みができた時点で結果を返させるようにしたければReadメソッド、指定した文字数を読み込めた時点で結果を返させるようにしたい場合にはReadBlockメソッドを使います。
こういった動作の違いについては、Stream.Readメソッドの動作についてもあわせてご覧ください。
StreamReaderとBOMの扱い
先頭にBOMの付いたストリームをStreamReaderで読み込む場合、ReadToEndやReadLineメソッドからはBOMが除去された文字列が返されます。 この動作はエンコーディングを指定した場合・省略した場合のどちらでも同じです。 ReadToEnd・ReadLine以外のメソッドでも同様にBOMの除去された文字列が返されます。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
// UTF-8でエンコードされた文字列をBOM付きでファイルに書き込む
File.WriteAllBytes("sample.txt", new byte[] {0xEF, 0xBB, 0xBF, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E}); // [BOM] + '日本語'
using (var reader = new StreamReader("sample.txt")) {
// encodingを指定せずに作成したStreamReaderのReadToEndで読み込む
Print(reader.ReadToEnd());
}
using (var reader = new StreamReader("sample.txt")) {
// encodingを指定せずに作成したStreamReaderのReadLineで読み込む
Print(reader.ReadLine());
}
using (var reader = new StreamReader("sample.txt", Encoding.UTF8)) {
// encodingを指定して作成したStreamReaderのReadToEndで読み込む
Print(reader.ReadToEnd());
}
using (var reader = new StreamReader("sample.txt", Encoding.UTF8)) {
// encodingを指定して作成したStreamReaderのReadLineで読み込む
Print(reader.ReadLine());
}
}
static void Print(string text)
{
Console.Write("Length = {0} : {1} ", text.Length, text);
foreach (var c in text) {
Console.Write("U+{0:X} ", (int)c);
}
Console.WriteLine();
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
' UTF-8でエンコードされた文字列をBOM付きでファイルに書き込む
File.WriteAllBytes("sample.txt", New Byte() {&hEF, &hBB, &hBF, &hE6, &h97, &hA5, &hE6, &h9C, &hAC, &hE8, &hAA, &h9E}) ' [BOM] + '日本語'
Using reader As New StreamReader("sample.txt")
' encodingを指定せずに作成したStreamReaderのReadToEndで読み込む
Print(reader.ReadToEnd())
End Using
Using reader As New StreamReader("sample.txt")
' encodingを指定せずに作成したStreamReaderのReadLineで読み込む
Print(reader.ReadLine())
End Using
Using reader As New StreamReader("sample.txt", Encoding.UTF8)
' encodingを指定して作成したStreamReaderのReadToEndで読み込む
Print(reader.ReadToEnd())
End Using
Using reader As New StreamReader("sample.txt", Encoding.UTF8)
' encodingを指定して作成したStreamReaderのReadLineで読み込む
Print(reader.ReadLine())
End Using
End Sub
Shared Sub Print(ByVal text As String)
Console.Write("Length = {0} : {1} ", text.Length, text)
For Each c As Char In text
Console.Write("U+{0:X} ", AscW(c))
Next
Console.WriteLine()
End Sub
End Class
Length = 3 : 日本語 U+65E5 U+672C U+8A9E Length = 3 : 日本語 U+65E5 U+672C U+8A9E Length = 3 : 日本語 U+65E5 U+672C U+8A9E Length = 3 : 日本語 U+65E5 U+672C U+8A9E
BOMを含めたまま読み込みを行いたい場合は、Streamから直接読み込むか、BinaryReaderクラスを使って読み込み処理を記述する必要があります。
StreamWriter
ここではStreamWriterの使い方について解説します。 このセクションで紹介するサンプルコードでは、簡単化のためファイル名を指定してStreamWriterを作成していますが、FileStreamやMemoryStreamなどのStreamを指定してStreamWriterのインスタンスを生成した場合も同じように動作します。 また、エンコーディングの指定も省略しています。
改行付きの書き込み (WriteLine)
WriteLineメソッドは、ストリームに1行分の文字列を書き込みます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamWriter writer = new StreamWriter("sample.txt")) {
// 1行目を書き込む
writer.WriteLine("line 1");
// 2行目を書き込む
writer.WriteLine("line 2");
// 3行目を書き込む
writer.WriteLine("line 3");
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using writer As New StreamWriter("sample.txt")
' 1行目を書き込む
writer.WriteLine("line 1")
' 2行目を書き込む
writer.WriteLine("line 2")
' 3行目を書き込む
writer.WriteLine("line 3")
End Using
End Sub
End Class
WriteLineメソッドでは、書き込みの際に改行文字が付け加えられます。 デフォルトでは改行文字としてプラットフォームの改行文字が使われますが、NewLineプロパティに値を設定することで使用する改行文字を変更することができます。
また、WriteLineメソッドに文字列以外を引数に指定した場合は、文字列に変換した上で書き込まれます。 複合書式設定を使用することで書式を指定したり0埋め・右詰め・左詰めした上で書き込むこともできます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamWriter writer = new StreamWriter("sample.txt")) {
// 数値を書き込む
writer.WriteLine(16);
writer.WriteLine(Math.PI);
// 現在の日付を書き込む
writer.WriteLine(DateTime.Now);
// 複数の値を一行に書き込む
writer.WriteLine("{0} {1} {2:hh:mm}", 42, 72, DateTime.Now);
// 書式を指定して値を書き込む
writer.WriteLine("|{0,10:D6}|", 12345); // 数値を幅10・6桁に0埋めして右揃え
writer.WriteLine("|{0,-10:D6}|", 12345); // 数値を幅10・6桁に0埋めして左揃え
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using writer As New StreamWriter("sample.txt")
' 数値を書き込む
writer.WriteLine(16)
writer.WriteLine(Math.PI)
' 現在の日付を書き込む
writer.WriteLine(DateTime.Now)
' 複数の値を一行に書き込む
writer.WriteLine("{0} {1} {2:hh:mm}", 42, 72, DateTime.Now)
' 書式を指定して値を書き込む
writer.WriteLine("|{0,10:D6}|", 12345) ' 数値を幅10・6桁に0埋めして右揃え
writer.WriteLine("|{0,-10:D6}|", 12345) ' 数値を幅10・6桁に0埋めして左揃え
End Using
End Sub
End Class
文字列への変換に際して、現在のスレッドのカルチャが書式プロバイダとして使用されます。 これはFormatProviderプロパティで参照可能です。 書式指定子や書式プロバイダについての詳細は以下のページをご覧ください。
改行無しの書き込み (Write)
Writeメソッドは、WriteLineメソッドとは異なり書き込みの際に改行文字が追記されません。 それ以外はWriteLineメソッドと同じです。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamWriter writer = new StreamWriter("sample.txt")) {
// 数値を書き込む
writer.Write(16);
writer.Write(" ");
writer.Write(Math.PI);
// 改行文字を書き込む
writer.Write(Environment.NewLine);
// 複数の値を一行に書き込む
writer.Write("{0} {1} {2:hh:mm}", 42, 72, DateTime.Now);
// 改行文字を書き込む
writer.Write(Environment.NewLine);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using writer As New StreamWriter("sample.txt")
' 数値を書き込む
writer.Write(16)
writer.Write(" ")
writer.Write(Math.PI)
' 改行文字を書き込む
writer.Write(Environment.NewLine)
' 複数の値を一行に書き込む
writer.Write("{0} {1} {2:hh:mm}", 42, 72, DateTime.Now)
' 改行文字を書き込む
writer.Write(Environment.NewLine)
End Using
End Sub
End Class
書き込む改行文字や改行の位置を細かく指定したい場合にはWriteメソッドを使うことができます。
改行文字
StreamWriterのWriteLineメソッドは、デフォルトではプラットフォームの改行文字を使用します(ランタイム・システム・プラットフォームの情報 §.改行文字)。 NewLineプロパティに値を設定することでStreamWriterが使用する改行文字を変更することができます。 NewLineプロパティではCR(\r)やLF(\n)以外の文字を含めたり、長さ0の文字列を設定することもできます。
using System;
using System.IO;
class Sample {
static void Main()
{
using (StreamWriter writer = new StreamWriter("sample.txt")) {
writer.WriteLine("line1");
writer.WriteLine("line2");
// 改行文字を変更する
// 改行を表す記号にプラットフォームの改行文字を付けたものを指定する
writer.NewLine = "↵" + Environment.NewLine;
writer.WriteLine("line3");
writer.WriteLine("line4");
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using writer As New StreamWriter("sample.txt")
writer.WriteLine("line1")
writer.WriteLine("line2")
' 改行文字を変更する
' 改行を表す記号にプラットフォームの改行文字を付けたものを指定する
writer.NewLine = "↵" + Environment.NewLine
writer.WriteLine("line3")
writer.WriteLine("line4")
End Using
End Sub
End Class
line1 line2 line3↵ line4↵
ファイルへの追記
StreamWriterのコンストラクタでファイル名を指定する場合、同時にファイルを追記モードで開くかどうかを指定することができるようになっています。 コンストラクタの引数appendにtrueを指定すれば、ファイルの末尾から書き込みを開始するようにしたStreamWriterインスタンスが作成されます。 省略した場合やfalseを指定した場合は上書きモードでファイルが開かれ、既存のファイルを開く場合にはその内容が破棄されます。
using System;
using System.IO;
class Sample {
static void Main()
{
// 上書きモードでsample.txtを開く
using (StreamWriter writer = new StreamWriter("sample.txt", false)) {
// 以下の内容はsample.txtに上書きされる
writer.WriteLine("line1");
writer.WriteLine("line2");
}
// 追記モードでsample.txtを開く
using (StreamWriter writer = new StreamWriter("sample.txt", true)) {
// 以下の内容はsample.txtに追記される
writer.WriteLine("line3");
writer.WriteLine("line4");
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' 上書きモードでsample.txtを開く
Using writer As New StreamWriter("sample.txt", False)
' 以下の内容はsample.txtに上書きされる
writer.WriteLine("line1")
writer.WriteLine("line2")
End Using
' 追記モードでsample.txtを開く
Using writer As New StreamWriter("sample.txt", True)
' 以下の内容はsample.txtに追記される
writer.WriteLine("line3")
writer.WriteLine("line4")
End Using
End Sub
End Class
line1 line2 line3 line4
Fileクラスを使った書き込み
書き込み先がファイルで、書き込む内容がすでに文字列として存在している場合は、Fileクラスのメソッドを使って書き込むこともできます。 File.WriteAllTextメソッドを使うと、指定した文字列を指定したファイルに書き込むことができます。 このメソッドを使うことでStreamWriterを作成せずにファイルへの書き込みが行えるため、書き込み処理の記述を簡略化することができます。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
// ファイルに書き込む内容
string content = @"line1
line2
line3
line4
";
// 文字列contentの内容をUTF-8でエンコードしてsample.txtに書き込む
File.WriteAllText("sample.txt", content, Encoding.UTF8);
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
' ファイルに書き込む内容
Dim content As String = "line1
line2
line3
line4
"
' 文字列contentの内容をUTF-8でエンコードしてsample.txtに書き込む
File.WriteAllText("sample.txt", content, Encoding.UTF8)
End Sub
End Class
line1 line2 line3 line4
File.WriteAllTextメソッドでは既存のファイルに対する書き込みの場合、ファイルの内容は上書きされますが、File.AppendAllTextメソッドを使うと既存のファイルの内容に追記することができます。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
// ファイルに上書きする内容
string content = @"line1
line2
";
// 文字列contentの内容をsample.txtに上書き
File.WriteAllText("sample.txt", content, Encoding.UTF8);
// ファイルに追記する内容
content = @"line3
line4
";
// 文字列contentの内容をsample.txtに追記
File.AppendAllText("sample.txt", content, Encoding.UTF8);
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
' ファイルに上書きする内容
Dim content As String = "line1
line2
"
' 文字列contentの内容をsample.txtに上書き
File.WriteAllText("sample.txt", content, Encoding.UTF8)
' ファイルに追記する内容
content = "line3
line4
"
' 文字列contentの内容をsample.txtに追記
File.AppendAllText("sample.txt", content, Encoding.UTF8)
End Sub
End Class
line1 line2 line3 line4
File.WriteAllLinesメソッドを使うと、文字列配列の各要素を1行としてファイルに書き込むことができます。 File.WriteAllLinesメソッドでは、書き込みの際に自動的に改行文字が付加されます。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
// ファイルに書き込む内容
string[] lines = new string[] {
"line1",
"line2",
"line3",
"line4",
};
// 配列linesの各要素を1行としてsample.txtに書き込む
File.WriteAllLines("sample.txt", lines, Encoding.UTF8);
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
' ファイルに書き込む内容
Dim lines() As String = New String() { _
"line1", _
"line2", _
"line3", _
"line4" _
}
' 配列linesの各要素を1行としてsample.txtに書き込む
File.WriteAllLines("sample.txt", lines, Encoding.UTF8)
End Sub
End Class
line1 line2 line3 line4
.NET Framework 4からは文字列配列だけでなく任意のIEnumerable<string>を指定することもできるようになっています。 そのため、書き込みたい内容がList<string>に格納されている場合でも配列に変換する必要はなくなりました。 また、.NET Framework 4からはFile.AppendAllLinesメソッドも追加されていて、このメソッドを使うことで文字列配列をファイルに追記することができます。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
// ファイルに上書きする内容
List<string> lines = new List<string>();
lines.Add("line1");
lines.Add("line2");
// linesの内容をsample.txtに上書き
File.WriteAllLines("sample.txt", lines, Encoding.UTF8);
// ファイルに追記する内容
lines.Clear();
lines.Add("line3");
lines.Add("line4");
// linesの内容をsample.txtに追記
File.AppendAllLines("sample.txt", lines, Encoding.UTF8);
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
' ファイルに上書きする内容
Dim lines As New List(Of String)()
lines.Add("line1")
lines.Add("line2")
' linesの内容をsample.txtに上書き
File.WriteAllLines("sample.txt", lines, Encoding.UTF8)
' ファイルに追記する内容
lines.Clear()
lines.Add("line3")
lines.Add("line4")
' linesの内容をsample.txtに追記
File.AppendAllLines("sample.txt", lines, Encoding.UTF8)
End Sub
End Class
line1 line2 line3 line4
Fileクラスのメソッドを使ったファイルの書き込みについてはファイル入出力を参照してください。
Fileクラスのメソッドを使ったインスタンスの作成
Fileクラスのメソッドを使ってFileStreamを作成できるのと同様、Fileクラスには指定したファイルを開いてStreamReader・StreamWriterを作成するためのメソッドがいくつか用意されています。
File.OpenTextメソッドを使うと、ファイル名を指定するだけでStreamReaderを作成することができます。 このメソッドを使うと、FileStreamインスタンスの作成を省略してStreamReaderを作成することができます。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtを読み込むStreamReaderを作成する
using (StreamReader reader = File.OpenText("sample.txt")) {
Console.WriteLine(reader.ReadLine());
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtを読み込むStreamReaderを作成する
Using reader As StreamReader = File.OpenText("sample.txt")
Console.WriteLine(reader.ReadLine())
End Using
End Sub
End Class
同様に、File.CreateTextメソッドを使うと、ファイル名を指定するだけでStreamWriterを作成することができます。 また、File.AppendTextメソッドを使えば、ファイルへの追記を行うStreamWriterを作成できます。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtに書き込むStreamWriterを作成する
using (StreamWriter writer = File.CreateText("sample.txt")) {
writer.WriteLine("line1");
writer.WriteLine("line2");
}
// ファイルsample.txtに追記を行うStreamWriterを作成する
using (StreamWriter writer = File.AppendText("sample.txt")) {
writer.WriteLine("line3");
writer.WriteLine("line4");
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtに書き込むStreamWriterを作成する
Using writer As StreamWriter = File.CreateText("sample.txt")
writer.WriteLine("line1")
writer.WriteLine("line2")
End Using
' ファイルsample.txtに追記を行うStreamWriterを作成する
Using writer As StreamWriter = File.AppendText("sample.txt")
writer.WriteLine("line3")
writer.WriteLine("line4")
End Using
End Sub
End Class
なお、これらのメソッドではエンコーディングを指定することはできないので、UTF-8以外で読み書きを行いたい場合はStreamReader・StreamWriterのコンストラクタを使用してインスタンスを作成する必要があります。
StreamReader・StreamWriterとベースとなるストリームのクローズ
StreamReader・StreamWriterでは、Closeメソッドを呼び出したりusingステートメントから抜けた際、ベースとなったストリームも閉じられ、ストリームに対するアクセスができなくなります。
using System;
using System.IO;
class Sample {
static void Main()
{
using (Stream stream = File.OpenRead("sample.txt")) {
// streamをベースにしてStreamReaderを作成
using (StreamReader reader = new StreamReader(stream)) {
reader.ReadLine();
}
// usingステートメントから抜ける時点でreader.Disposeメソッドが呼び出される
// その際、readerが使用していたstreamもCloseされるため、その後streamのメソッドを
// 呼び出そうとするとObjectDisposedExceptionがスローされる
stream.ReadByte();
}
using (Stream stream = File.OpenWrite("sample.txt")) {
// streamをベースにしてStreamWriterを作成
using (StreamWriter writer = new StreamWriter(stream)) {
writer.WriteLine("text");
}
// StreamWriterの場合も同様に使用していたstreamはCloseされるため、
// ObjectDisposedExceptionがスローされる
stream.WriteByte(0);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using stream As Stream = File.OpenRead("sample.txt")
' streamをベースにしてStreamReaderを作成
Using reader As New StreamReader(stream)
reader.ReadLine()
End Using
' Usingステートメントから抜ける時点でreader.Disposeメソッドが呼び出される
' その際、readerが使用していたstreamもCloseされるため、その後streamのメソッドを
' 呼び出そうとするとObjectDisposedExceptionがスローされる
stream.ReadByte()
End Using
Using stream As Stream = File.OpenWrite("sample.txt")
' streamをベースにしてStreamWriterを作成
Using writer As New StreamWriter(stream)
writer.WriteLine("text")
End Using
' StreamWriterの場合も同様に使用していたstreamはCloseされるため、
' ObjectDisposedExceptionがスローされる
stream.WriteByte(0)
End Using
End Sub
End Class
StreamReader・StreamWriterを使用したあとも引き続きベースとなったストリームを使い続けたい場合は、usingステートメントは使わず、使用したStreamReader・StreamWriterを閉じないようにします。
ただ、StreamWriterでは閉じる際に同時にフラッシュも行われるようになっているため、usingステートメントやCloseメソッドでStreamWriterを閉じない場合は、StreamWriterを使い終わった時点でFlushメソッドを呼び出し、書き込んだ内容をフラッシュさせるようにする必要があります。
using System;
using System.IO;
class Sample {
static void Main()
{
using (Stream stream = File.OpenRead("sample.txt")) {
// usingステートメントを使わずにStreamReaderを作成・使用する
StreamReader reader = new StreamReader(stream);
reader.ReadLine();
// StreamReaderを使用した後に、streamを直接使用する
stream.ReadByte();
}
using (Stream stream = File.OpenWrite("sample.txt")) {
// usingステートメントを使わずにStreamWriterを作成・使用する
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("text");
// StreamWriterで書き込んだ内容をフラッシュする
writer.Flush();
// StreamWriterを使用した後に、streamを直接使用する
stream.WriteByte(0);
}
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
Using stream As Stream = File.OpenRead("sample.txt")
' Usingステートメントを使わずにStreamReaderを作成・使用する
Dim reader As New StreamReader(stream)
reader.ReadLine()
' StreamReaderを使用した後に、streamを直接使用する
stream.ReadByte()
End Using
Using stream As Stream = File.OpenWrite("sample.txt")
' Usingステートメントを使わずにStreamWriterを作成・使用する
Dim writer As New StreamWriter(stream)
writer.WriteLine("text")
' StreamWriterで書き込んだ内容をフラッシュする
writer.Flush()
' StreamWriterを使用した後に、streamを直接使用する
stream.WriteByte(0)
End Using
End Sub
End Class
.NET Framework 4.5以降では、コンストラクタの引数leaveOpenにtrueを指定すると、StreamReader・StreamWriterを閉じてもベースとなるストリームを開いたままにすることができます。 ただし、leaveOpenを指定する場合は、同時に引数encoding、detectEncodingFromByteOrderMarksおよびbufferSizeも省略せずに指定する必要があります。
using System;
using System.IO;
using System.Text;
class Sample {
static void Main()
{
using (Stream stream = File.OpenRead("sample.txt")) {
// leaveOpenにtrueを指定してStreamReaderを作成
const bool leaveOpen = true;
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8, false, 1024, leaveOpen)) {
reader.ReadLine();
}
// usingステートメントから抜けてreaderが閉じられても
// streamは閉じられないのでObjectDisposedExceptionはスローされない
stream.ReadByte();
}
using (Stream stream = File.OpenWrite("sample.txt")) {
// leaveOpenにtrueを指定してStreamWriterを作成
const bool leaveOpen = true;
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, leaveOpen)) {
writer.WriteLine("text");
}
// StreamWriterの場合も同様で、ObjectDisposedExceptionはスローされない
stream.WriteByte(0);
}
}
}
Imports System
Imports System.IO
Imports System.Text
Class Sample
Shared Sub Main()
Using stream As Stream = File.OpenRead("sample.txt")
' leaveOpenにtrueを指定してStreamReaderを作成
Const leaveOpen As Boolean = True
Using reader As New StreamReader(stream, Encoding.UTF8, False, 1024, leaveOpen)
reader.ReadLine()
End Using
' Usingステートメントから抜けてreaderが閉じられても
' streamは閉じられないのでObjectDisposedExceptionはスローされない
stream.ReadByte()
End Using
Using stream As Stream = File.OpenWrite("sample.txt")
' leaveOpenにtrueを指定してStreamWriterを作成
Const leaveOpen As Boolean = True
Using writer As New StreamWriter(stream, Encoding.UTF8, 1024, leaveOpen)
writer.WriteLine("text")
End Using
' StreamWriterの場合も同様で、ObjectDisposedExceptionはスローされない
stream.WriteByte(0)
End Using
End Sub
End Class
ベースとなるストリームの扱いについてはBinaryReader・BinaryWriterでも同様で、.NET Framework 4.5以降ではBinaryReader・BinaryWriterにおいても使用するストリームを開いたままにするかどうかをコンストラクタで指定できるようになっています。
直接ファイル名を指定してインスタンスを作成した場合や、File.OpenTextなどのメソッドを使ってインスタンスを作成した場合は、かならず作成したStreamReader/StreamWriterを閉じるようにしなければなりません。 これらのメソッドではFileStreamが内部的に作成されますが、StreamReader/StreamWriterから閉じないかぎり内部的に作成されたFileStreamは閉じられません。 従って、StreamReader/StreamWriterによって閉じるまではファイルは開かれたままとなるため、この間他からファイルへのアクセスを行うことができなくなります。
using System;
using System.IO;
class Sample {
static void Main()
{
// ファイルsample.txtに書き込むためのStreamWriterを作成
StreamWriter writer = new StreamWriter("sample.txt");
writer.WriteLine("Hello, world!");
// usingステートメントを使わず、Closeメソッドも呼び出さない場合、
// sample.txtを開いているStreamWriter内部のFileStreamは開かれたままとなる
//writer.Close();
// ファイルsample.txtから読み込むためのStreamReaderを作成
// (sample.txtは上記のStreamWriterが開いたままのため、
// StreamReaderは開くことができずIOExceptionがスローされる)
StreamReader reader = new StreamReader("sample.txt");
Console.WriteLine(reader.ReadLine());
}
}
Imports System
Imports System.IO
Class Sample
Shared Sub Main()
' ファイルsample.txtに書き込むためのStreamWriterを作成
Dim writer As New StreamWriter("sample.txt")
writer.WriteLine("Hello, world!")
' usingステートメントを使わず、Closeメソッドも呼び出さない場合、
' sample.txtを開いているStreamWriter内部のFileStreamは開かれたままとなる
'writer.Close()
' ファイルsample.txtから読み込むためのStreamReaderを作成
' (sample.txtは上記のStreamWriterが開いたままのため、
' StreamReaderは開くことができずIOExceptionがスローされる)
Dim reader As New StreamReader("sample.txt")
Console.WriteLine(reader.ReadLine())
End Sub
End Class