Streamをコンストラクタの引数に取るクラスをusingでネストすると、コード分析でCA2202で警告されます。
using (Stream stream = new FileStream("sample.txt", FileMode.Create)) using (StreamWriter writer = new StreamWriter(stream)) { // writerオブジェクトを使用する処理 } // warning CA2202: Microsoft.Usage : オブジェクト 'stream' は、メソッド '***' 内で 2 回以上破棄される可能性があります。System.ObjectDisposedException の生成を回避するには、オブジェクトに対して Dispose を 2 回以上呼び出さないようにしてください。
よってtry-finallyで破棄するようにします。
Stream stream = null; try { stream = new FileStream("sample.txt", FileMode.Create); using (StreamWriter writer = new StreamWriter(stream)) { stream = null; // StreamWriterの生成に成功したならばそのDispose()で破棄されるため、Stream.Dispose()が呼ばれないようにnullを設定 // writerオブジェクトを使用する処理 } } finally { if (stream != null) stream.Dispose(); }
次のようにすると、”ABC”という文字列を含む"sample.txt"というファイルが作成されます。
string path = "sample.txt"; using (FileStream fs = new FileStream(path, FileMode.Create)) { byte[] array = new byte[] { 65, 66, 67 }; // ABC fs.Write(array, 0, array.Length); } // ここでストリームが閉じられる
ストリームは閉じられるときに書き込まれますので、usingなどで明示的にリソースを解放します。
public FileStream ( string path, System.IO.FileMode mode, System.Security.AccessControl.FileSystemRights rights, System.IO.FileShare share, int bufferSize, System.IO.FileOptions options, System.Security.AccessControl.FileSecurity fileSecurity );FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions, FileSecurity) - FileStream コンストラクター (System.IO) | Microsoft Learn
FileStreamのインスタンスは、次のようにFile.Create()メソッドでも作成できます。
FileStream fs = File.Create(path)File.Create メソッド (String) (System.IO) | MSDN
modeを指定したときに「パス '***' へのアクセスが拒否されました。」としてUnauthorizedAccessExceptionが投げられるならば、それに適合するようにshareも指定します。
メソッド | |
---|---|
CopyTo(Stream) | 現在のストリームから、別のストリームへ書き込む |
Read(Byte[], Int32, Int32) | 指定位置から指定バイト数だけ読み取り、指定のバッファへ書き込む |
ReadByte() | 現在位置から1バイト読み取り、それを返す |
Write(Byte[], Int32, Int32) | |
WriteByte(Byte) |
メソッド | |
---|---|
CopyToAsync(Stream) | |
ReadAsync(Byte[], Int32, Int32) | |
WriteAsync(Byte[], Int32, Int32) | |
BeginRead(Byte[], Int32, Int32, AsyncCallback, Object) | 非推奨。代わりにReadAsyncを使用する |
BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object) | 非推奨。代わりにWriteAsyncを使用する |
public override int Read( byte[] array, int offset, int count )FileStream.Read メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
[ComVisibleAttribute(false)] [HostProtectionAttribute(SecurityAction.LinkDemand, ExternalThreading = true)] public Task<int> ReadAsync( byte[] buffer, int offset, int count )Stream.ReadAsync メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
public override void Write( byte[] array, int offset, int count )FileStream.Write メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
[ComVisibleAttribute(false)] [HostProtectionAttribute(SecurityAction.LinkDemand, ExternalThreading = true)] public Task WriteAsync( byte[] buffer, int offset, int count )Stream.WriteAsync メソッド (Byte[], Int32, Int32) (System.IO) | MSDN
public async void Method() { // await式に到達するまでは、同期的に実行される using (FileStream fs = new FileStream("sample.txt", FileMode.Create)) { byte[] array = new byte[1000 * 1000]; await fs.WriteAsync(array, 0, array.Length); // ここで呼び出し元へ制御が戻る // 書き込みが完了すると、これ以降が処理される } }
awaitで待たせないと、書き込み完了前にストリームが閉じられることがあります。ファイル アクセスにおける非同期の使用 (C#) | Microsoft Learn
他のプロセスからの読み取りと書き込みを防止できます。FileStream.Lock(Int64, Int64) メソッド (System.IO) | Microsoft Learn
string path = "sample.dat";
FileStream fs1 = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
FileStream fs2 = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
fs1.Lock(0, 1);
fs2.ReadByte(); // IOException「プロセスはファイルにアクセスできません。別のプロセスがファイルの一部をロックしています。」
配列がbyte型ならば、Write(Byte[])などのバイト配列向けのメソッドだけで処理できます。しかしそれ以外の型ならば、要素を1つずつ処理します。
int[] items = new int[] { 1, 2, 3 }; // 書き込み FileStream fileStream = null; try { fileStream = File.Create("sample.dat"); using (BinaryWriter binaryWriter = new BinaryWriter(fileStream)) { fileStream = null; foreach (int item in items) { binaryWriter.Write(item); } } } finally { if (fileStream != null) fileStream.Dispose(); } // 読み取り int[] result; fileStream = null; try { fileStream = File.OpenRead("sample.dat"); using (BinaryReader binaryReader = new BinaryReader(fileStream)) { fileStream = null; long length = binaryReader.BaseStream.Length / sizeof(int); result = new int[length]; for (int i = 0; i < length; i++) { result[i] = binaryReader.ReadInt32(); } } } finally { if (fileStream != null) fileStream.Dispose(); }
public MemoryStream ();MemoryStream() - MemoryStream Constructor (System.IO) | Microsoft Learn
byte[] src = new byte[] { 1, 2, 3 }; byte[] dest; using (MemoryStream memoryStream = new MemoryStream()) { // 書き込む memoryStream.Write(src, 0, src.Length); // ストリームの位置を先頭へ戻す memoryStream.Seek(0, SeekOrigin.Begin); // 読み込む dest = new byte[memoryStream.Length]; int count = memoryStream.Read(dest, 0, dest.Length); }
ストリームの全体の内容を、他のストリームへ書き込めます。
public virtual void WriteTo (System.IO.Stream stream);MemoryStream.WriteTo(Stream) Method (System.IO) | Microsoft Learn
内部ではStream.Write()で書き込まれます。WriteTo - memorystream.cs
[System.CLSCompliant(false)] [System.Security.SecurityCritical] public UnmanagedMemoryStream ( byte* pointer, // アンマネージド メモリへのポインタ long length, // 使用するメモリの長さ long capacity, // ストリームに割り当てられたメモリの総量 System.IO.FileAccess access // FileAccessの値 );UnmanagedMemoryStream(Byte*, Int64, Int64, FileAccess) - UnmanagedMemoryStream Constructor (System.IO) | Microsoft Learn
byte[] array = { 1, 2, 3 }; int size = Marshal.SizeOf(array[0]) * array.Length; IntPtr intPtr = Marshal.AllocHGlobal(size); unsafe { // IntPtrからbyteポインタを取得する byte* bytePtr = (byte*)intPtr.ToPointer(); // アンマネージド メモリへのポインタを使用するUnmanagedMemoryStreamオブジェクトを作成する using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(bytePtr, array.Length, array.Length, FileAccess.Write)) { // データを書き込む stream.Write(array, 0, array.Length); // ストリームを閉じる stream.Close(); } } Marshal.FreeHGlobal(intPtr);
プリミティブ型のバイナリでの書き込みと、指定のエンコーディングでの文字列の書き込みに対応しています。
// 書き込み FileStream fileStream = null; try { fileStream = File.Open("sample.dat", FileMode.Create); using (BinaryWriter binaryWriter = new BinaryWriter(fileStream)) { fileStream = null; binaryWriter.Write(10); // 0x0a 0x00 0x00 0x00 binaryWriter.Write(1.0f); // 0x00 0x00 0x80 0x3F binaryWriter.Write(true); // 0x01 binaryWriter.Write("ab"); // 0x02 0x61 0x62 } } finally { if (fileStream != null) fileStream.Dispose(); }BinaryWriter クラス (System.IO) | MSDN
// 読み取り FileStream fileStream = null; try { fileStream = File.Open("sample.dat", FileMode.Open); using (BinaryReader binaryReader = new BinaryReader(fileStream)) { fileStream = null; int a1 = binaryReader.ReadInt32(); // 10 float a2 = binaryReader.ReadSingle(); // 1 bool a3 = binaryReader.ReadBoolean(); // true string a4 = binaryReader.ReadString(); // "ab" // すべてのデータの読み取り byte[] result = new byte[binaryReader.BaseStream.Length]; binaryReader.Read(result, 0, result.Length); } } finally { if (fileStream != null) fileStream.Dispose(); }BinaryReader クラス (System.IO) | MSDN
ReadString()で読み取った文字列の先頭には、文字列の長さが付加されます。これが期待するものでなければReadBytes()でbyte配列として読み取り、Encoding.GetString()で文字列にデコードします。
BaseStreamプロパティからStreamを取得し、そのPositionがLengthより小さいことで末尾に達していないことを確認できます。binaryfiles - C# checking for binary reader end of file - Stack Overflow
Stream stream = binaryReader.BaseStream;
while(stream.Position < stream.Length)
{
// 読み取り処理
}
public StreamWriter( string path )StreamWriter コンストラクター (String) (System.IO) | MSDN
ファイルが存在する場合には上書きされます。これを追記するようにするには、次の形式でappendをtrueとします。
public StreamWriter(
string path,
bool append // 追記するかどうか
)
StreamWriter コンストラクター (String, Boolean) (System.IO) | MSDN
既定では文字エンコーディングはUTF-8となります。これを変更するには、encodingの指定を追加します。
public StreamWriter( string path, bool append, // 追記するかどうか Encoding encoding // 文字エンコーディング )StreamWriter コンストラクター (String, Boolean, Encoding) (System.IO) | MSDN
string path = "sample.txt"; using (StreamWriter sw = new StreamWriter(path)) { sw.Write("ABC"); }
メソッド | 機能 |
---|---|
Write(String) | |
Write(String, Object) | 書式設定してストリームへ書き込める |
WriteAsync(String) | 非同期で書き込める |
WriteLine(String) | 行の終端記号も書き込める |
public override void Write( string value )StreamWriter.Write メソッド (String) (System.IO) | MSDN
public StreamReader( string path )StreamReader コンストラクター (String) (System.IO) | MSDN
型 | プロパティ | |
---|---|---|
Encoding | CurrentEncoding | 現在の文字エンコーディング |
bool | EndOfStream | ストリームの末尾に達しているならば、true |
戻り値の型 | メソッド | 読み込み基準 | 読み込み範囲 | 末尾から読み込んだときの戻り値 |
---|---|---|---|---|
int | Read() | ストリームの現在位置 | 1文字 | -1 |
int | Read(Char[], Int32, Int32) | ストリームの指定位置 | 指定文字数 | 0 |
int | ReadBlock(Char[], Int32, Int32) | ストリームの指定位置 | 指定文字数 | 0 |
string | ReadLine() | ストリームの現在位置 | 1行 | null |
string | ReadToEnd() | ストリームの現在位置 | 末尾まで | "" |
戻り値の型 | メソッド |
---|---|
Task<int> | ReadAsync(Char[], Int32, Int32) |
Task<int> | ReadBlockAsync(Char[], Int32, Int32) |
Task<string> | ReadLineAsync() |
Task<string> | ReadToEndAsync() |
\n、\r、\rnまでが1行と見なされます。 Remarks - StreamReader.ReadLine Method (System.IO) | Microsoft Learn ReadLine - streamreader.cs
public override string ReadToEnd ();StreamReader.ReadToEnd メソッド (System.IO) | Microsoft Learn
using (StreamReader sr = new StreamReader("samle.txt")) { string text = sr.ReadToEnd(); }c# - Difference between StreamReader.Read and StreamReader.ReadBlock - Stack Overflow
ストリームが末尾に達していないときにこのメソッドを呼び出すと、無期限にブロックされます。
このクラスはIDisposableを実装しますが、それで処置するリソースはないため、Dispose()を呼ぶ必要はありません。Remarks - StringWriter Class (System.IO) | Microsoft Learn
文字コードは既定でSystem.Text.UnicodeEncodingとなるため、これを変更するにはこのクラスを継承し、Encodingプロパティから希望する文字コードを返します。xml - XmlWriter encoding UTF-8 using StringWriter in C# - Stack Overflow
StringReaderは、文字列からしか初期化できません。
public StringReader( string s )StringReader(String) コンストラクター (System.IO) | Microsoft Learn
このクラスはIDisposableを実装しますが、それで処置するリソースはないため、Dispose()を呼ぶ必要はありません。Remarks - StringReader Class (System.IO) | Microsoft Learn
書き込み用のクラスなどは用意されていないため、自前で処理します。c# - Best practices for serializing objects to a custom string format for use in an output file - Stack Overflow
定義に従うならばエスケープの処理などが必要ですが、簡易で良ければString.Join()でカンマで連結し、StreamWriterのWriteLine()でファイルに書き込めます。
string[][] data = new string[][] { new string[]{ "a", "b", "c" }, new string[]{ "d", "e" } }; using (StreamWriter streamWriter = new StreamWriter("sample.csv")) { foreach (string[] item in data) { streamWriter.WriteLine(String.Join(",", item)); } }
TextFieldParserクラスで読み込めます。
Microsoft.VisualBasic.FileIO.TextFieldParser parser = new Microsoft.VisualBasic.FileIO.TextFieldParser("sample.csv"); // 区切り文字を設定する。これは既定ではnullとなっており、設定しなければ読み込み時に「区切り記号が Nothing または empty であるため、区切り記号で分けられたフィールドを読み取れません。」としてArgumentExceptionが投げられる parser.SetDelimiters(","); List<string[]> result = new List<string[]>(); while (!parser.EndOfData) { // 現在の行から、指定形式でフィールドを読み込む string[] fields = parser.ReadFields(); result.Add(fields); } // 結果を配列として得たいならば、リストを配列に変換する string[][] array = result.ToArray();
TextFieldParserの利用時に「型または名前空間の名前 'FileIO' が名前空間 'Microsoft.VisualBasic' に存在しません」としてエラーとなるときには、Microsoft.VisualBasicを参照に追加します。
型 | プロパティ | 内容 | 既定値 |
---|---|---|---|
string[] | CommentTokens | コメント行と見なす文字列 | string[0] |
FieldType | TextFieldType | テキストファイルの形式 | FieldType.Delimited |
string[] | Delimiters | 区切り記号。TextFieldTypeがDelimitedのときに必須 | null |
int[] | FieldWidths | テキストファイルの各列の幅。TextFieldTypeがFixedWidthのときに必須 | null |
bool | TrimWhiteSpace | trueならば、前後の空白を除去する | true |
Delimitersにカンマ以外を設定すれば、CSV以外の形式にも対応できます。またTextFieldTypeをFixedWidthとしてFieldWidthsでその幅を指定すると、固定幅でも読み込めます。
列挙子 | フィールドの形式 |
---|---|
Delimited | 区切り文字で分割 |
FixedWidth | 固定幅で分割 |
現在の行のすべてのフィールドを、文字列の配列として得られます。そして次にデータが含まれる行までカーソルが進みます。
public string[] ReadFields()TextFieldParser.ReadFields メソッド (Microsoft.VisualBasic.FileIO) | MSDN
StringReader stringReader = new StringReader("AA,\"BB,BB\",\"C\"\"C\"\r\nDD,\"EE\r\nEE\""); TextFieldParser parser = new TextFieldParser(stringReader); parser.SetDelimiters(","); string[] fields1 = parser.ReadFields(); // AA, BB,BB, C\"C string[] fields2 = parser.ReadFields(); // DD, EE\r\nEE
ReadFields()では空行が読み飛ばされるため、それが期待する動作ではないならば、行を追加するなどの処理が必要です。.net - Why does TextFieldParser.ReadField remove consecutive newlines from middle of a field? - Stack Overflow
これと類似するメソッドとは、下表のような違いがあります。
戻り値 | メソッド | 機能 | 構文解析 | カーソル | 空行 |
---|---|---|---|---|---|
string[] | ReadFields() | 現在の行を文字配列として読み込む | あり | 次の行へ進む | 無視する |
string | ReadLine() | 現在の行を文字列として読み込む | なし | 無視しない | |
string | PeekChars(Int32) | 指定数の文字を読み込む | 進まない | 無視する |
複雑な操作が必要ならば、XmlDocumentを用います。c# - XmlDocument vs XmlWriter - Stack Overflow
.NET 2.0以降はXmlTextWriterの代わりに、このXmlWriterクラスを用います。Remarks - XmlTextWriter Class (System.Xml) | Microsoft Learn
public static System.Xml.XmlWriter Create ( string outputFileName, System.Xml.XmlWriterSettings settings );Create(String, XmlWriterSettings) - XmlWriter.Create Method (System.Xml) | Microsoft Learn
XMLの作成方法はsettingsで指定します。XmlWriterSettings Class (System.Xml) | Microsoft Learn
XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter writer = XmlWriter.Create("sample.xml", settings)) { writer.WriteComment("SAMPLE"); writer.WriteStartElement("root"); writer.WriteStartElement("e1"); writer.WriteAttributeString("a1", "10"); writer.WriteStartElement("e2"); writer.WriteAttributeString("a2", "20"); writer.WriteString("ABC"); writer.WriteEndElement(); writer.WriteStartElement("e2"); writer.WriteAttributeString("a3", "30"); writer.WriteString("DEF"); writer.WriteEndElement(); writer.WriteEndElement(); writer.WriteEndElement(); }
このコードは次のようなファイルを出力します。
<?xml version="1.0" encoding="utf-8"?> <!--SAMPLE--> <root> <e1 a1="10"> <e2 a2="20">ABC</e2> <e2 a3="30">DEF</e2> </e1> </root>
最後にClose()を呼ぶ必要がありますが、これはDispose()でも呼ばれます。
public abstract void WriteStartDocument (bool standalone);WriteStartDocument(Boolean) - XmlWriter.WriteStartDocument Method (System.Xml) | Microsoft Learn
standaloneにtrueを渡すことで、XML宣言に"standalone=yes"を追記できます。XML宣言をより柔軟に出力するには、WriteProcessingInstruction()を用います。
public abstract void WriteProcessingInstruction ( string name, string text );XmlWriter.WriteProcessingInstruction(String, String) Method (System.Xml) | Microsoft Learn
.NET 2.0以降はXmlTextReaderの代わりに、このXmlReaderクラスを用います。Remarks - XmlTextReader Class (System.Xml) | Microsoft Learn
public static System.Xml.XmlReader Create ( System.IO.Stream input, // XMLデータを含むストリーム System.Xml.XmlReaderSettings settings // 新しいXmlReaderインスタンスの設定 );Create(Stream, XmlReaderSettings) - XmlReader.Create メソッド (System.Xml) | Microsoft Learn
settingsを省略したときはDefault settingsの設定値が用いられます。
using (XmlReader reader = XmlReader.Create("sample.xml")) { reader.MoveToContent(); Console.Write(reader.Name); // "root" reader.ReadToFollowing("e2"); Console.Write(reader.GetAttribute("a2")); // "20" Console.Write(reader.ReadElementContentAsString()); // "ABC" XmlNodeType type1 = reader.NodeType; // Whitespace reader.Skip(); XmlNodeType type2 = reader.NodeType; // Element Console.Write(reader.GetAttribute("a3")); // "30" Console.Write(reader.ReadElementContentAsString()); // "DEF" }
現在のノードとその子孫ノードを読み込める新しいXmlReaderを得られます。これによりアクセスできる範囲を制限できます。
public virtual System.Xml.XmlReader ReadSubtree ();XmlReader.ReadSubtree メソッド (System.Xml) | Microsoft Learn
サブツリー全体が読み込まれると、 Read()はfalseを返します。このメソッドから得られるXmlReaderが閉じられると、元のXmlReaderはサブツリーのノードのEndElementの位置になります。
新しいXmlReaderを閉じるまで、元のXmlReaderを操作しないようにします。そのような操作には対応しておらず、予期せぬ結果となります。Remarks - XmlReader.ReadSubtree Method (System.Xml) | Microsoft Learn
圧縮したファイルをツールで展開できるようにするには、DeflateStreamではなくGZipStreamを用います。c# - GZipStream or DeflateStream class? - Stack Overflow
FileInfo file = new FileInfo("sample.txt"); FileInfo newFile = new FileInfo(file.FullName + ".gz"); using (FileStream original = file.OpenRead()) // 圧縮前のファイル using (FileStream compressed = newFile.Create()) // 圧縮後のファイル using (GZipStream gzip = new GZipStream(compressed, CompressionMode.Compress)) { original.CopyTo(gzip); }
これを展開するには、CompressionModeをDecompressとします。
FileInfo file = new FileInfo("sample.txt.gz"); FileInfo newFile = new FileInfo("sample.txt"); using (FileStream compressed = file.OpenRead()) // 展開前のファイル using (FileStream decompressed = newFile.Create()) // 展開後のファイル using (GZipStream gzip = new GZipStream(compressed, CompressionMode.Decompress)) { gzip.CopyTo(decompressed); }
ZIPファイルへストリームから書き込むには、次のようにします。c# - Creating a ZIP Archive in Memory Using System.IO.Compression - Stack Overflow
using (FileStream fileStream = new FileStream("sample.zip", FileMode.Create)) using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Create)) { // File 1 ZipArchiveEntry archiveEntry1 = archive.CreateEntry("file1.txt"); using (StreamWriter stream = new StreamWriter(archiveEntry1.Open())) { stream.Write("sample1"); } // File 2 ZipArchiveEntry archiveEntry2 = archive.CreateEntry("file2.txt"); using (StreamWriter stream = new StreamWriter(archiveEntry2.Open())) { stream.Write("sample2"); } }
逆にストリームへ読み込むには、次のようにします。
using (FileStream fileStream = new FileStream("sample.zip", FileMode.Open)) using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { // File 1 ZipArchiveEntry archiveEntry1 = archive.GetEntry("file1.txt"); using (StreamReader stream = new StreamReader(archiveEntry1.Open())) { string str = stream.ReadLine(); // "sample1" } // File 2 ZipArchiveEntry archiveEntry2 = archive.GetEntry("file2.txt"); using (StreamReader stream = new StreamReader(archiveEntry2.Open())) { string str = stream.ReadLine(); // "sample2" } }