「C Sharpの基礎 - ファイル」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
 
(同じ利用者による、間の5版が非表示)
1行目: 1行目:
== 概要 ==
C#では、<code>System.IO</code>名前空間を使用することにより、ファイルの読み書き、作成、削除等の基本的な操作を行うことができる。<br>
<br>
テキストファイルの読み込みには、主に<code>StreamReader</code>クラスや<code>File.ReadAllText</code>メソッドが使用される。<br>
これらを使用することにより、テキストファイルの内容を簡単に文字列として取得できる。<br>
<br>
テキストファイルへの書き込みには、主に<code>StreamWriter</code>クラスや<code>File.WriteAllText</code>メソッドが使用される。<br>
<br>
バイナリファイルを扱う場合は、<code>FileStream</code>クラスを使用する。<br>
これにより、バイトデータの読み書きが可能になる。<br>
また、非同期操作を行う場合は、<code>ReadAsync</code>メソッドや<code>WriteAsync</code>メソッド等を使用することで、効率的にファイル操作を行うことができる。<br>
<br>
ファイルパスの操作には、<code>Path</code>クラスが役立つ。<br>
このクラスを使用することで、ファイル名の取得や拡張子の変更、ディレクトリの結合等の操作が簡単に行える。<br>
<br>
セキュリティの観点からは、ファイルアクセス権限の設定が重要である。<br>
<code>FileInfo</code>クラスや<code>DirectoryInfo</code>クラスを使用して、ファイルやディレクトリのアクセス権限を確認したり変更することができる。<br>
<br>
エラーハンドリングについて、ファイル操作時には様々な例外が発生する可能性があるため、try-catchブロックを使用して適切に対処することが基本である。<br>
<br>
また、大容量ファイルを扱う場合は、メモリ効率を考慮する必要がある。<br>
ファイルを1度に全て読み込むのではなく、ストリームを使用して少しずつ読み込むことにより、メモリ使用量を抑えることができる。<br>
<br><br>
== バイナリファイル ==
== バイナリファイル ==
==== バイナリファイルの読み込みと書き込み ====
==== バイナリファイルの読み込みと書き込み ====
ファイルの内容をすべてbyte配列に読み込む、または、byte配列全体をファイルに書き込む場合、<br>
ファイルの内容をすべてbyte配列に読み込む、または、byte配列全体をファイルに書き込む場合、<br>
Fileクラス(System.IO名前空間)のReadAllBytesメソッドおよびWriteAllBytesメソッドを使用する。<br>
<code>File</code>クラス(<code>System.IO</code>名前空間)の<code>ReadAllBytes</code>メソッドおよび<code>WriteAllBytes</code>メソッドを使用する。<br>
これらのメソッド(静的)では、ファイルを開く、byte配列への読み込みまたはbyte配列の書き込み、ファイルを閉じるという3つの処理を<br>
<br>
ReadAllBytesメソッドで実行可能である。<br>
これらのメソッド(静的)では、"ファイルを開く"、"byte配列への読み込みまたはbyte配列の書き込み"、"ファイルを閉じる"という3つの処理を<br>
ReadAllBytesメソッドは、読み込むファイルのパス名をパラメータに指定して呼ぶと、戻り値としてファイルの内容を読み込んだbyte配列を返す。<br>
<code>ReadAllBytes</code>メソッドで実行可能である。<br>
また、WriteAllBytesメソッドでは、書き込むファイルのパス名(ファイルが存在する場合は上書き)と書き込むbyte配列をパラメータで指定する。<br>
<code>ReadAllBytes</code>メソッドは、読み込むファイルのパス名をパラメータに指定して呼ぶと、戻り値としてファイルの内容を読み込んだbyte配列を返す。<br>
尚、WriteAllBytesメソッドの戻り値はない。<br>
<br>
  <source lang="cpp">
また、<code>WriteAllBytes</code>メソッドでは、書き込むファイルのパス名(ファイルが存在する場合は上書き)と書き込むbyte配列をパラメータで指定する。<br>
なお、<code>WriteAllBytes</code>メソッドの戻り値は無い。<br>
  <syntaxhighlight lang="c#">
  byte[] data = File.ReadAllBytes(@"hoge.bin"); // 読み込み
  byte[] data = File.ReadAllBytes(@"hoge.bin"); // 読み込み
  File.WriteAllBytes(@"newhoge.bin", data);    // 書き込み
  File.WriteAllBytes(@"newhoge.bin", data);    // 書き込み
  </source>
  </syntaxhighlight>
<br>
<br>
==== サンプルコード(バイナリファイルを固定サイズで分割する) ====
==== バイナリファイルを固定サイズで分割する ====
次のサンプルコードは、ReadAllBytesメソッドとWriteAllBytesメソッドを使用したサンプルコードである。<br>
以下の例では、<code>File</code>クラスの<code>ReadAllBytes</code>メソッドと<code>WriteAllBytes</code>メソッドを使用している。<br>
このプログラムでは、バイナリファイルを読み込み、それを固定サイズ(サイズ : 1[MB])の複数ファイルに分割して出力する。<br>
バイナリファイルを読み込み、それを固定サイズ(1[MB])の複数ファイルに分割して出力している。<br>
尚、出力する最後のファイルは固定サイズに満たない場合がある。<br>
<br>
なお、出力する最後のファイルは固定サイズに満たない場合がある。<br>
  <syntaxhighlight lang="C#">
  <syntaxhighlight lang="C#">
  using System;
  using System;
51行目: 78行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
ReadAllBytesメソッドは、ファイル全体を一度に読み込むため、巨大なファイルを扱う場合には、<br>
<u>※注意</u><br>
読み込みに掛かる時間や使用するメモリ量について注意が必要である。<br>
<u>ReadAllBytesメソッドは、ファイル全体を一度に読み込むため、</u><br>
<u>サイズの大きいファイルを扱う場合は、読み込みに掛かる時間や使用するメモリ量について注意が必要である。</u><br>
<br>
<br>
==== サンプルコード(非同期でバイナリファイルを読み書きする) ====
==== 非同期でバイナリファイルを読み書きする ====
バイナリファイルへ書き込んだ後、それを読み込むサンプルコードに示す。<br>
以下の例では、バイナリファイルへ書き込んだ後、そのバイナリファイルを読み込んでいる。<br>
この例では、書き込み用に開くときにFileMode.Createを指定しているので上書きになる。(追記する場合 : FileMode.Append)<br>
バイナリファイルを書き込み用として開く時に<code>FileMode.Create</code>を指定しているため、上書きになる。(追記する場合は、<code>FileMode.Append</code>)<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  async Task BinaryReadWriteAsync(byte[] data)
  public async Task BinaryReadWriteAsync(byte[] data)
  {
  {
     const string FilePath = @".\sample.dat";  // 読み書きするファイル
     const string FilePath = @".\sample.dat";  // 読み書きするファイル


     // バイナリファイルへ書き込み
     // バイナリファイルとして書き込む
     // ファイルを上書きモードで開く(ファイルが存在しない場合は作成)
     // ファイルを上書きモードで開く(ファイルが存在しない場合は作成する)
     // 追加モードにするにはFileModeをAppendに変える
     // 追記モードにするには、FileMode.Appendに変更する
     using (var fs = new FileStream(FilePath, FileMode.Create, FileAccess.Write))
     using (var fs = new FileStream(FilePath, FileMode.Create, FileAccess.Write))
     {
     {
244行目: 272行目:
<br><br>
<br><br>


== ファイルを確実に削除する ==
== ファイルの削除 ==
==== 読み取り専用ファイルの削除 ====
<code>System.IO.File</code>クラスの<code>Delete</code>メソッドを使用する場合、読み取り専用ファイルに対して実行すると例外が発生する。<br>
<code>System.IO.File</code>クラスの<code>Delete</code>メソッドを使用する場合、読み取り専用ファイルに対して実行すると例外が発生する。<br>
これを解決するには、ファイルを削除する前に、ファイルの読み取り専用属性を解除する。<br>
これを解決するには、ファイルを削除する前に、ファイルの読み取り専用属性を解除する。<br>
281行目: 310行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
== パーミッション ==
.NET 5以降では、<code>System.IO.FileSystem</code>名前空間に<code>UnixFileMode</code>列挙型が追加された。<br>
この列挙型を使用して、UNIX系OSのファイルパーミッションを扱うことができる。<br>
<br>
<u>ただし、<code>UnixFileMode</code>プロパティはLinux環境でのみ動作することに注意する。</u><br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.IO;
class FilePermissions
{
    static void Main(string[] args)
    {
      // lsコマンドのファイルパス
      string filePath = $"/usr/bin/ls";
      // ファイルが存在するかどうかを確認
      if (!File.Exists(filePath))
      {
          Console.WriteLine("指定されたファイルが存在しない");
          return;
      }
      try
      {
          // ファイルの情報を取得
          FileInfo fileInfo = new FileInfo(filePath);
          // Unixファイルモードを取得
          UnixFileMode mode = fileInfo.UnixFileMode;
          // パーミッションを8進数で表示
          Console.WriteLine($"ファイル: {filePath}");
          Console.WriteLine($"パーミッション: {Convert.ToString((int)mode, 8).PadLeft(4, '0')}");
          // 詳細なパーミッション情報を表示
          Console.WriteLine("詳細:");
          Console.WriteLine($"ユーザ: {((mode & UnixFileMode.UserRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.UserWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.UserExecute) != 0 ? "x" : "-")}");
          Console.WriteLine($"グループ: {((mode & UnixFileMode.GroupRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.GroupWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.GroupExecute) != 0 ? "x" : "-")}");
          Console.WriteLine($"その他:  {((mode & UnixFileMode.OtherRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.OtherWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.OtherExecute) != 0 ? "x" : "-")}");
      }
      catch (Exception ex)
      {
          Console.WriteLine($"エラーが発生: {ex.Message}");
      }
    }
}
</syntaxhighlight>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:C_Sharp]]
[[カテゴリ:C_Sharp]]

2024年9月16日 (月) 14:39時点における最新版

概要

C#では、System.IO名前空間を使用することにより、ファイルの読み書き、作成、削除等の基本的な操作を行うことができる。

テキストファイルの読み込みには、主にStreamReaderクラスやFile.ReadAllTextメソッドが使用される。
これらを使用することにより、テキストファイルの内容を簡単に文字列として取得できる。

テキストファイルへの書き込みには、主にStreamWriterクラスやFile.WriteAllTextメソッドが使用される。

バイナリファイルを扱う場合は、FileStreamクラスを使用する。
これにより、バイトデータの読み書きが可能になる。
また、非同期操作を行う場合は、ReadAsyncメソッドやWriteAsyncメソッド等を使用することで、効率的にファイル操作を行うことができる。

ファイルパスの操作には、Pathクラスが役立つ。
このクラスを使用することで、ファイル名の取得や拡張子の変更、ディレクトリの結合等の操作が簡単に行える。

セキュリティの観点からは、ファイルアクセス権限の設定が重要である。
FileInfoクラスやDirectoryInfoクラスを使用して、ファイルやディレクトリのアクセス権限を確認したり変更することができる。

エラーハンドリングについて、ファイル操作時には様々な例外が発生する可能性があるため、try-catchブロックを使用して適切に対処することが基本である。

また、大容量ファイルを扱う場合は、メモリ効率を考慮する必要がある。
ファイルを1度に全て読み込むのではなく、ストリームを使用して少しずつ読み込むことにより、メモリ使用量を抑えることができる。


バイナリファイル

バイナリファイルの読み込みと書き込み

ファイルの内容をすべてbyte配列に読み込む、または、byte配列全体をファイルに書き込む場合、
Fileクラス(System.IO名前空間)のReadAllBytesメソッドおよびWriteAllBytesメソッドを使用する。

これらのメソッド(静的)では、"ファイルを開く"、"byte配列への読み込みまたはbyte配列の書き込み"、"ファイルを閉じる"という3つの処理を
ReadAllBytesメソッドで実行可能である。
ReadAllBytesメソッドは、読み込むファイルのパス名をパラメータに指定して呼ぶと、戻り値としてファイルの内容を読み込んだbyte配列を返す。

また、WriteAllBytesメソッドでは、書き込むファイルのパス名(ファイルが存在する場合は上書き)と書き込むbyte配列をパラメータで指定する。
なお、WriteAllBytesメソッドの戻り値は無い。

 byte[] data = File.ReadAllBytes(@"hoge.bin"); // 読み込み
 File.WriteAllBytes(@"newhoge.bin", data);     // 書き込み


バイナリファイルを固定サイズで分割する

以下の例では、FileクラスのReadAllBytesメソッドとWriteAllBytesメソッドを使用している。
バイナリファイルを読み込み、それを固定サイズ(1[MB])の複数ファイルに分割して出力している。

なお、出力する最後のファイルは固定サイズに満たない場合がある。

 using System;
 using System.IO;
 
 class BinarySplit
 {
    static void Main()
    {
       // バイナリファイルの読み込み
       byte[] src = File.ReadAllBytes(@"hoge.bin");
 
       int FILESIZE = 1024 * 1024; // 分割サイズ

       int num = 0;
 
       for (int remain = src.Length; remain > 0; remain -= FILESIZE)
       {
          int length = Math.Min(FILESIZE, remain);  // 作成する分割ファイルの実際のサイズ

          byte[] dest = new byte[length];           // 分割ファイルへ書き出すbyte配列の作成
          Array.Copy(src, num * FILESIZE, dest, 0, length);
 
          // 出力ファイル名(out0001.bin、out0002.bin、……)
          string name = String.Format("out{0:D4}.bin", num + 1);

          // byte配列のファイルへの書き込み
          File.WriteAllBytes(name, dest);

          num++;
       }
    }
 }


※注意
ReadAllBytesメソッドは、ファイル全体を一度に読み込むため、
サイズの大きいファイルを扱う場合は、読み込みに掛かる時間や使用するメモリ量について注意が必要である。

非同期でバイナリファイルを読み書きする

以下の例では、バイナリファイルへ書き込んだ後、そのバイナリファイルを読み込んでいる。
バイナリファイルを書き込み用として開く時にFileMode.Createを指定しているため、上書きになる。(追記する場合は、FileMode.Append)

 public async Task BinaryReadWriteAsync(byte[] data)
 {
    const string FilePath = @".\sample.dat";  // 読み書きするファイル

    // バイナリファイルとして書き込む
    // ファイルを上書きモードで開く(ファイルが存在しない場合は作成する)
    // 追記モードにするには、FileMode.Appendに変更する
    using (var fs = new FileStream(FilePath, FileMode.Create, FileAccess.Write))
    {
       await fs.WriteAsync(data, 0, data.Length);  // バイナリデータを非同期で書き込む
    }
 
    // バイナリファイル読み込み
    byte[] result;  // データを格納する配列

    // ファイルを読み取りモードで開く
    using (var fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
    {
       result = new byte[fs.Length];  // データ格納用の配列を確保する
 
       // バイナリデータを非同期で読み込む
       await fs.ReadAsync(result, 0, (int)fs.Length);
    }

    // 読み込んだ内容をコンソールへ出力する
    for (int i=0; i < result.Length; i++)
    {
       Write($"{result[i]:X2} ");
       if (i % 16 == 7)
       {
          Write(" ");
       }
       else if (i % 16 == 15)
       {
          WriteLine();
       }
    }
    WriteLine();
 }



テキストファイル

テキストファイルの読み込みと書き込み

StreamReaderクラスを使用して、
テキストファイルの読み込みと書き込み、using構文によるファイルのクローズ、例外処理等を記載する。

テキストファイルの内容を読み込むには、以下の3つの処理からなる。

  1. ファイルを開く
  2. ファイルの内容を読み込む
  3. ファイルを閉じる


テキストファイルを開く

ファイルを開くにはいくつかの方法が存在するが、UTF-8のファイルを開く場合、
StreamReaderクラス(System.IO名前空間)を使用する。

 StreamReader sr = new StreamReader(@"readme.txt", Encoding.UTF8);


また、Shift-JISのファイルを開く場合は、以下のように記述する。

 StreamReader sr = new StreamReader(@"readme.txt", Encoding.GetEncoding("Shift_JIS"));


テキストファイルを読み込む

StreamReaderクラスのReadToEndメソッドを使用して、テキストファイルの内容を一括して読み込むことができる。
また、ReadToEndメソッドでは、改行文字等も含めて、全ての1つの文字列として読み込まれる。

usingステートメントを使用してファイルを閉じる

StreamReaderクラスは、IDisposableインタフェースを実装しているため、(スーパークラスのTextReaderクラスが実装している)
Closeメソッドの代わりに、usingステートメントを利用することもできる。

usingステートメントを抜ける時、自動的にStreamReaderクラスのDisposeメソッドを呼び出して、
その中でCloseメソッドが呼び出される。

ファイルが読み込めない時のエラー処理

ファイルが存在しない場合やファイルの読み込み中にディスク等に何らかの障害が発生する場合、
例外が発生してプログラムの実行が中断されるため、例外処理を記述する必要がある。

テキストファイルの一括読み込み(変数の使用)

以下の例では、ReadToEndメソッドを非同期処理に対応したReadToEndAsyncメソッドを使用して、
ファイルの全ての内容を一括して読み込んでいる。

 using System;
 using System.IO;
 using System.Text;
 
 string text = string.Empty;
 
 try
 {
    using (StreamReader sr = new StreamReader(@"hoge.txt", Encoding.UTF8))
    {
       text = await sr.ReadToEndAsync();
    }

    Console.Write(text); 
 }
 catch (Exception ex)
 {
    Console.WriteLine(ex.Message);
 }


ファイルの内容を1行ずつ読み込む

ファイルの全ての内容を1つの変数に読み込むよりも、1行ずつ読み込んで文字列の配列に代入する方が扱いやすい場合がある。

ファイルから1行読み込むには、StreamReaderクラスのReadLineメソッドを使用する。
ReadLineメソッドは、ファイルから1行のみ読み込み、内部で読み取り位置を1行分進める。
読み取り位置がファイルの末尾に達した場合、nullを返す。

なお、.NET Framework 2.0以降では、Fileクラス(System.IO名前空間)において、
ファイルを開く・内容を読み込む・ファイルを閉じるという一連の処理を1つのメソッドで行うReadAllTextメソッドとReadAllLinesメソッドが追加されている。

 using System;
 using System.IO;
 using System.Text;
 using System.Collections;
 
 string line = string.Empty;
 List<string> list = new List<string>();
 
 try
 {
    using (StreamReader sr = new StreamReader(@"hoge.txt", Encoding.UTF8))
    {
       while ((line = await sr.ReadLineAsync()) != null)
       {
          list.Add(line);
       }
    }
 }
 catch (Exception ex)
 {
    Console.WriteLine(ex.Message);
 }
 
 foreach (string strLine in list)
 {
    Console.WriteLine(strLine);
 }


テキストファイルの一括読み込み(変数の使用)

テキストファイルの全ての内容を1つの変数に読み込む場合、FileクラスのReadAllTextメソッドを使用する。
ReadAllTextメソッドで使用する標準の文字コードは、UTF-8である。

以下の例では、テキストファイルの内容を一括して変数に読み込んでいる。

 using System.Text;
 using System.IO;
 
 try
 {  // ファイルの読み込み
    string text1 = File.ReadAllText(@"hoge.txt", Encoding.UTF8);
 }
 catch (Exception ex)
 {
    Console.WriteLine(ex.Message);
 }


テキストファイルの一括読み込み(配列の使用)

テキストを1行ずつ処理する場合、各行を要素とする文字列配列を使用する。
これは、System.IO.FileクラスのReadAllLinesメソッドを使用して実行する。
ReadAllLinesメソッドで使用する標準の文字コードは、UTF-8である。

なお、ReadAllLinesメソッドは、ファイル末尾の改行文字を無視する。
ファイル末尾に改行文字がある場合、ReadAllLinesメソッドで得られる行数は1行少なくなる。

以下の例では、テキストファイルの内容を一括して配列に読み込んでいる。

 using System.Text;
 using System.IO;
 
 try
 {  // ファイルの読み込み
    string[] lines1 = File.ReadAllLines(@"hoge.txt", Encoding.UTF8);
 }
 catch (Exception ex)
 {
    Console.WriteLine(ex.Message);
 }



ファイルの削除

読み取り専用ファイルの削除

System.IO.FileクラスのDeleteメソッドを使用する場合、読み取り専用ファイルに対して実行すると例外が発生する。
これを解決するには、ファイルを削除する前に、ファイルの読み取り専用属性を解除する。

以下の例では、ファイルの読み取り専用属性を解除して、ファイルを削除している。

 public static void main(string[] args)
 {
    // 読み取り専用ファイルの場合でも削除する
    DeleteFile(@"C:\Hoge\");
 }
 
 /// -----------------------------------------------------------------------------
 /// <summary>
 ///     指定したファイルを削除する</summary>
 /// <param name="stFilePath">
 ///     削除するファイルまでのパス</param>
 /// -----------------------------------------------------------------------------
 public static void DeleteFile(string stFilePath)
 {
    System.IO.FileInfo cFileInfo = new System.IO.FileInfo(stFilePath);
 
    // ファイルが存在しているか判断する
    if(cFileInfo.Exists)
    {
       // 読み取り専用属性がある場合は、読み取り専用属性を解除する
       if((cFileInfo.Attributes & System.IO.FileAttributes.ReadOnly) == System.IO.FileAttributes.ReadOnly)
       {
          cFileInfo.Attributes = System.IO.FileAttributes.Normal;
       }
 
       // ファイルを削除する
       cFileInfo.Delete();
    }
 }



パーミッション

.NET 5以降では、System.IO.FileSystem名前空間にUnixFileMode列挙型が追加された。
この列挙型を使用して、UNIX系OSのファイルパーミッションを扱うことができる。

ただし、UnixFileModeプロパティはLinux環境でのみ動作することに注意する。

 using System;
 using System.IO;
 
 class FilePermissions
 {
    static void Main(string[] args)
    {
       // lsコマンドのファイルパス
       string filePath = $"/usr/bin/ls";
 
       // ファイルが存在するかどうかを確認
       if (!File.Exists(filePath))
       {
          Console.WriteLine("指定されたファイルが存在しない");
          return;
       }
 
       try
       {
          // ファイルの情報を取得
          FileInfo fileInfo = new FileInfo(filePath);
 
          // Unixファイルモードを取得
          UnixFileMode mode = fileInfo.UnixFileMode;
 
          // パーミッションを8進数で表示
          Console.WriteLine($"ファイル: {filePath}");
          Console.WriteLine($"パーミッション: {Convert.ToString((int)mode, 8).PadLeft(4, '0')}");
 
          // 詳細なパーミッション情報を表示
          Console.WriteLine("詳細:");
          Console.WriteLine($"ユーザ: {((mode & UnixFileMode.UserRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.UserWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.UserExecute) != 0 ? "x" : "-")}");
          Console.WriteLine($"グループ: {((mode & UnixFileMode.GroupRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.GroupWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.GroupExecute) != 0 ? "x" : "-")}");
          Console.WriteLine($"その他:   {((mode & UnixFileMode.OtherRead) != 0 ? "r" : "-")}" +
                            $"{((mode & UnixFileMode.OtherWrite) != 0 ? "w" : "-")}" +
                            $"{((mode & UnixFileMode.OtherExecute) != 0 ? "x" : "-")}");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 }