C#で学んだこと
step9



ファイルの必要性

ファイルを使えば、
  • プログラムが終了したあとも、コンピュータの電源を落としたあとも、データは永続的に保存される。
  • ファイルの内容は、同じプログラムあるいは別のプログラムによって、何度も繰り返し利用できる。
  • ファイルを使えば、大量のデータを便利に扱うことができる。
p.342

ストリームの基本とファイル入出力

ストリームはプログラムの入出力を扱う。 ストリームは、System.IO名前空間にあるストリームクラス郡から実体化 ⇒例えば、Console.WriteLine()も、System.IOストリームクラス郡のメソッド
p.342

テキストファイル と バイナリファイル

テキストファイル テキストファイルは、私たち人間が読むことのできるファイル(Unicodeによって符号化) バイナリファイル バイナリファイルは、たいがいコンピュータプログラムによって生成処理される。 C#プログラムは、「バイナリストリーム」を介して、バイナリファイルの入出力を行う。 ⇒バイナリファイルは、プログラムで効率よく読み書きできる。 テキストファイルは、「文字ストリーム」を介して、アクセス・操作される。 ⇒効率が良くない。
p.343

System.IO名前空間の基本的なクラス(抜粋)

  • FileInfo ファイルを更新したり、ファイルに関する情報を収集することができる。 または新規ファイル作成、既存ファイルを削除することもできる。
  • FileInfoオブジェクトそのものが、ストリームオブジェクトを表現するのでなく、(操作するイメージ) FileInfoのOpenText()や、CreateText()から返される、 StreamReaderオブジェクトやStreamWriterオブジェクトが、 FileInfoが表現するファイルをアクセスするためのストリームを提供する。
  • StreamReader StreamWriter 文字をベースとする入出力ストリーム
  • StreamReaderクラスのインスタンスは、テキストファイルからテキストを読むのに使用できる。 StreamWriterクラスのインスタンスは、テキストファイルへテキストを書くのに使用できる。
  • FileStream バイナリファイルの読み書きに使われる
  • テキストファイルとバイナリファイルの両方の読み書きに使える。 ※テキストファイルを使うことが事前に分かっている場合、 StreamReaderやStreamWriterを使うほうが良い。
p.344

FileInfoクラス

FileInfoオブジェクトを作成する時、ファイル名(string型)をコンストラクタに渡すことで、 そのオブジェクトが扱うファイルを指定しなければいけない。 FileInfo 既存ファイル = new FileInfo("@:\MyTestFiles\MyFairytale.txt"); ※引数にがあるのは、パス名の指定で¥を書くとき、本来なら\\と書くところを、単に¥と書くことができるから。 FileInfoクラスのメンバー(抜粋)
  • CopyTo() FileInfoオブジェクトが表現するファイルを新しいファイルにコピー
  • Delete() FileInfoオブジェクトが表現するファイルを削除
  • FullNameプロパティ FileInfoオブジェクトが表現する完全なファイル名パスを含む長い名前)を返す。
  • Nameプロパティ FileInfoオブジェクトが表現するファイルの名前パスを含まない短い名前)を返す。
  • Lengthプロパティ FileInfoオブジェクトが表現するファイルのサイズ(バイト数)を返す。
  • CreatTimeプロパティ ファイルの作成日時を返す
  • CreateText() 新しいテキストファイルに書き込みを行うStreamWriterを作成
  • OpenText() 既存のテキスト ファイルからの読み取りを行う、 UTF8 エンコーディングの StreamReader を作成します。
  • OpenWrite() 書き込み専用の FileStream を作成します。
  • OpenRead() 読み取り専用の FileStream を作成します。
p.345

ファイルを扱う際の注意点

FileInfoや、System.IOを使う際、注意が必要。 それは、例えば間違ったファイル名や間違ったコマンドを与えると、 重要なファイル消したり損傷を与える危険があるから。 重要なファイルは常にバックアップしておくべき!
p.345

FileInfoクラスを使ったコード例

次のコードが書かれた、MyFairytale.txtがあったとする。 Once upon a time there was a beautiful princess MyFairytale.txt ////////////////////////////////////////////////////////////////////// using System; using System.IO; class FileInspector { public static void Main() { try { FileInfo 既存ファイル = new FileInfo( "@:\MyTestFiles\MyFairytale.txt"); Console.WriteLine("ファイル名: " + 既存ファイル.Name); Console.WriteLine("フルネーム: " + 既存ファイル.FullName); Console.WriteLine("長さ:{0}バイト" , 既存ファイル.Length); Console.WriteLine("作成日時:" + 既存ファイル.CreationTime); Console.WriteLine("MyFairytale.txtをYourFairytale.txtにコピーします"); 既存ファイル.CopyTo(@"C:\MyTestFiles\YourFairytale.txt" , true); //上書きの場合true Console.WriteLine("MyFairytale.txtを削除します"); 既存ファイル.Delete(); } catch (IOException exObj) { Console.WriteLine(exObj); } } } 結果::: ファイル名: MyFairytale.txt フルネーム: C:\MyTestFiles\YourFairytale.txt 長さ:50バイト 作成日時:2002/07/22 21:44:14 MyFairytale.txtをYourFairytale.txtにコピーします MyFairytale.txtを削除します ////////////////////////////////////////////////////////////////////// 今井さんは、コレでエラーの場所を示すlogファイルを作っていたんだ!!
p.346

FileInfoクラスを使う か Fileクラスを使うか

System.IO空間には、FileInfoクラスとほとんど同じ機能を提供するFileクラスがある。 FileInfoクラスとは対照的に、Fileクラスのメンバーはstatic宣言されているので、 Fileクラスオブジェクトを作成せず、ファイルの操作ができる。 例えばあるテキストファイルを別テキストファイルにコピーする場合、次のように書く。 File.Copy(@"C:\MyTestFiles\MyFairytale.txt" , @"C:\MyTestFiles\YourFairytale.txt"); ではなぜ、わざわざFileInfoクラスを使うのだろうか? ⇒Fileクラスのメソッドを呼び出すとき、その都度プロセッサへ  負荷の高いセキュリティチェック毎回行っている。 ⇒FileInfoクラスならば、そのセキュリティチェックは、オブジェクトが作成される時のみ  なので、メソッド呼び出し等、オブジェクト実体化以降の処理はセキュリティチェックなしで実行される。
p.346

完全なファイル名 と 相対ファイル名

ルートディレクトリから、ファイルへの完全なパス(絶対パス)を完全なファイル名。 カレントディレクトリに対する「相対」的なファイル名を、相対ファイル名。 つまり、ファイル保存のパスが相対ファイル名で書かれていたら、 ファイルの保存はカレントディレクトリに左右されて場所が決まる。
p.347

StreamReader と StreamWriterによる テキストファイルの入出力

StreamReaderとStreamWriterクラスは、文字ストリームを表現するために設計された専用クラスであり、 テキストファイル(のみ)の読み書きを便利に行うためのメソッド群を含むClose()がどちらのクラスにも含まれていて、 呼び出すと、対象のストリームを閉じる。 ⇒つまり、そのストリームに対して割り当てられていたリソース(アクセス権等)が解放され、  扱っていたファイルを、プログラムの他の部分がアクセスできるようになる。 ※プログラムの実行終了後で、ストリームをクローズせずにおくと、  ストリームは損傷を受けやすい。 Close()を使って明示的にクローズしなくても、 プログラムが正常終了すれば、自動的にクローズされるが、 異常終了した場合、自動的にクローズする機会を失ってしまう。 なので、ストリームを使い終わったら常に、できるだけ早くストリームをクローズすべき。 StreamWriterクラスの主なメンバー
  • Close() 出力ストリームをクローズし、関連リソースを解放する
  • Write() 改行文字を挿入することなく、1個以上の文字をストリームに書く 数多くの多重定義がある。
  • WriteLine() 1個以上の文字をストリームに書き、改行文字を挿入する
StreamReaderクラスの主なメンバー
  • Close() 入力ストリームをクローズし、関連リソースを解放する
  • Read() 次の1文字(あるいは文字列)を入力ストリームから読む 2つだけ多重定義がある。
  • ReadLine() 次の1行の文字列を読み、stringとして返す。 EOF(End Of File : ファイルの終わり)に到達したらnullを返す 付け加えると、次の改行文字まで読み込み、それまでに読み込んだ文字列をstringとして返す

StreamWriterオブジェクトである、myStreamWriterを使うと、
MyStreamWriter.WriteLine("この3行は"); MyStreamWriter.WriteLine("すべて1つの"); MyStreamWriter.WriteLine("ファイルに入る"); ↓↓ 改行は(NL = NewLine)として扱われ、次のようになる。 /////コード例///////////////////////////////////////////////////////// using System; using System.IO; class TextReaderWriter { public static void WriteTextToFile() { string テキスト行; StreamWriter 出力ストリーム = null; try { FileInfo textFile = new FileInfo(@"C:\MyTestFiles\MyStory.txt"); 出力ストリーム = textFile.CreateText(); //出力ストリームに、特定ファイルへのアクセス権を渡す //このタイミングでファイルを開く //ファイルが新規作成される。 //もし同じパスのファイルがあった場合、破棄して新規作成 Console.WriteLine("テキストファイルに書き込みます"); Console.WriteLine("3行のテキストを書いてください\n"); for (int i=0 ; i < 3; i++) { テキスト行 = Console.ReadLine(); //ストリームに書く内容をRead 出力ストリーム.WriteLine(テキスト行); //ストリームに書き込み } } catch (IOException exObj) { Console.WriteLine(exObj); } finally { 出力ストリーム.Close(); //こうやって書けば必ずクローズされる } } public static void ReadTextFromFile() { string テキスト行; StreamReader 入力ストリーム = null; try { FileInfo textFile = new FileInfo(@"C:\MyTestFiles\MyStory.txt"); 入力ストリーム = textFile.OpenText(); Console.WriteLine("\nテキストファイルから読み込みます: \n"); テキスト行 = 入力ストリーム.ReadLine(); //次の改行までの文字列を返す while(テキスト行 != null) //EOFにならない限り { Console.WriteLine(テキスト行); //改行までの文字列をプリント テキスト行 = 入力ストリーム.ReadLine(); } } catch(IOException exObj) { Console.WriteLine(exObj); } finally { 入力ストリーム.Close(); } } } class Tester { public static void Main() { TextReaderWriter.WriteTextToFile(); TextReaderWriter.ReadTextFromFile(); } } 結果::: テキストファイルに書き込みます 3行のテキストを書いてください それは雨の降る夜だった。<enter> 遠くで雷が鳴っていた。<enter> 突然コウモリが墓場を飛んだ。<enter> テキストファイルから読み込みます。 それは雨の降る夜だった。 遠くで雷が鳴っていた。 突然コウモリが墓場を飛んだ。 //////////////////////////////////////////////////////////////////////
p.349

FileStreamクラスによるバイナリ出力

FileStreamクラスは、バイナリストリームクラス。 文字ストリームにも使うことはできるが、文字ストリーム専用の便利なメソッドは含まれていない。 FileStreamクラスの主なメンバー
  • Close() ストリームをクローズし、関連リソースを解放する
  • ReadyByte() ストリームから1バイトを読む
  • WriteByte() ストリームに1バイトを書く byte型の引数を1つ取り、新しいファイルに書く
  • Lengthプロパティ ストリームの長さ(バイト数)を返す
////////////////////////////////////////////////////////////////////// using System; using System.IO; class TestBytesInOut { public static void WriteBytes() { FileStream 出力ストリーム = null; try { FileInfo bytesFile = new FileInfo(@"C:\MyTestFiles\numbers1.dat"); //numbers1.datを新規作成 出力ストリーム = bytesFile.OpenWrite(); for (byte i = 0 ; i < 10 ; i++) 出力ストリーム.WriteByte(i); } catch (IOException exObj) { Console.WriteLine(exObj); } finally { 出力ストリーム.Close(); } } public static void ReadBytes() { int 合計 = 0; int temp; FileStream 入力ストリーム = null; try { FileInfo numberFile = new FileInfo(@"C:\MyTestFiles\number1.dat"); 入力ストリーム = numberFile.OpenRead(); for (int i = 0 ; i < 入力ストリーム.Length ; i++) { temp = 入力ストリーム.ReadByte(); Console.WriteLine(temp); 合計 += temp; } Console.WriteLine("\nファイルの数値の合計:{0}" , 合計); } catch(IOException exObj) { Console.WriteLine(exObj); } finally { 入力ストリーム.Close(); } } } class Tester { public static void Main() { TestBytesInOut.WriteBytes(); TestBytesInOut.ReadBytes(); } } 結果::: 長さ:10 ファイルの数値のリスト: 0 1 2 3 4 5 6 7 8 9 ファイルの数値の合計:45 ////////////////////////////////////////////////////////////////////// 新しいFileStreamオブジェクトを作成するには、 @FileStreamのコンストラクタを使うか、。 AFileInfoのメソッドを使う 上コードでは、Aを使っている。 バイナリコードを扱うファイルはdatファイルを用いる?
p.351

ストリームやら、FileInfoクラスやらの、自分的まとめ

@FileInfo型オブジェクトを作成し、作成時のコンストラクタでファイルのパスを指定する(ファイル名も) Aストリームクラスのオブジェクトを実体化する。 BFileInfoオブジェクトのOpenText()やら、その他を使って、ストリームクラスと結び付けをする。 CストリームのオブジェクトからClose()を呼び出し、アクセス権やリソースを解放する。
p.--

p.

p.

p.

p.

p.

p.

p.

p.

p.

p.