ラベル StreamReader の投稿を表示しています。 すべての投稿を表示
ラベル StreamReader の投稿を表示しています。 すべての投稿を表示

2018年3月12日月曜日

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

画面表示とマウスカーソルが一段落付いたので、データを読み込む為の要素技術である、ファイルの読み込みの学習を始めました。

昔のCであれば、テキストファイルを読み込んで画面表示しようとしたら、
#include <stdio.h>
#define MAXLINE 256

FILE *fp;
char *fname = "file.txt";
char readline[MAXLINE];

if( (fp = fopen( fname, "r" ) ) == NULL ) {
 fprintf( stderr, "file %s cannot open.\n", fname );
 exit(-1);
}

whiel( fgets( readline, MAXLINE, fp ) != NULL ) {
 fputs( readline );
}

fclose( fp );

等と言うことになるんだと思います。上記についてはあえて詳しく説明はしませんが、file.txtというファイルをオープンして、1行ずつ読み込み、標準出力に1行ずつ出力するというものです。unixのキャラクタベースの世界だと、あえてファイルオープンをせずに、標準入力から入力を読む、というコードにしておいて、入力をファイルにリダイレクトする、というような思想で書くことが多かったと思います。

C#でテキストファイルをオープンして読み込む方法を探したら、以下のようなものが見つかりました。

下記リストは、とあるCSVファイルをオープンして、ファイルを1行ずつ読んで配列に格納し、格納し終わったらコンソールに出力しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Collections;


namespace read1line
{
    class Program
    {
        static void Main(string[] args)
        {
            string line = "";
            ArrayList al = new ArrayList();

            try {
                using (StreamReader sr = new StreamReader("c:/users/ぐすたふ/検討用テストデータ.csv",
                    Encoding.GetEncoding("Shift_JIS")))
                {
                    while((line = sr.ReadLine()) != null)  // ファイルを1行ずつ読んで配列alに格納
                    {
                        al.Add(line);
                    }
                }
            }
            catch (Exception e )
            {
                Console.WriteLine(e.Message);  // 例外が発生したら、コンソールにメッセージを出力
            }

            for( int i = 0; i < al.Count; i++ )  // alを1行ずつコンソールに出力
            {
                Console.WriteLine(al[i]);
            }
            Console.WriteLine("readed " + al.Count + "lines");
            Console.ReadKey();
        }
    }
}

ここで、 StreamReaderはSystem.IO名前空間に属するクラスです。文字コードを指定して、テキストファイルを読み込むためのクラスです。マイクロソフトの解説ページはこちらです。

いつものように機械翻訳で意味が分かりにくいので、もっとわかりやすく解説したページはこちらです。いつも勉強させていただいているdobon.net様です。

                using (StreamReader sr = new StreamReader("c:/users/ぐすたふ/検討用テストデータ.csv",
                    Encoding.GetEncoding("Shift_JIS")))

のコードで、ファイルを読み込む為のオブジェクトを生成しています。この中でファイルをオープンして文字コードの指定をしています。

                    while((line = sr.ReadLine()) != null)  // ファイルを1行ずつ読んで配列alに格納

の、StreamReader.ReadLine()(リスト中ではsr.ReadLine())は、テキストを1行読み込むメソッドです。

なお、リスト中に出てくるArrayListは、動的に大きさを拡大することが可能な配列を提供するクラスです。リストの冒頭に、テキストを読み込んで配列に格納する、と書きましたが、サイズが未知のテキストファイルを1行ずつ配列に読み込む為に、このArrayListを使っています。

ArrayListの説明はこちらにあります。

今度は、ファイルを最後まで一気にstring型の変数に読み込んで、一気にコンソールに出力しています。

/*
 * テキストファイルを一気に読み込む
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string text = "";
            try
            {
                using (StreamReader sr = new StreamReader("c:/users/ぐすたふ/検討用テストデータ.csv", 
                            Encoding.GetEncoding("shift-jis")))
                {
                    text = sr.ReadToEnd();  // ファイルを一気に最後まで読んで、textに格納
                }
                Console.Write(text);  // textをコンソールに一気に出力
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);  // 例外が発生したら、コンソールにメッセージを出力
            }

            Console.ReadKey();
        }
    }
}



こちらで出てくる

                    text = sr.ReadToEnd();  // ファイルを一気に最後まで読んで、textに格納

の中のStreamReader.ReadToEnd()(リスト中ではsr.ReadToEnd())は、ファイルの現在の場所からファイルの最後までを一気に読み込むメソッドです。


上記2つのリストでは、いずれの例も、usingステートメントと、try 〜 catchステートメントを使っています。

usingステートメントは、マイクロソフトのこのページに説明がありますが、機械翻訳なので全くわかりませんね。このページ(C# Tips様)このページ(プログラムマーズ雑記帳様)を読むと、もう少しわかりやすいと思います。

C#のクラスには、使い終わった後にDisposeメソッドを使ってリソースを開放しなければならないものがあります。

しかし、例外の発生によってDisposeの呼び出しが適切なタイミングでできないような場合、動作に矛盾が生じてしまう場合があるようです。それを防ぐ為、適切なDisposeを確実に行ってくれるようにするのがusingである、と書いてあります。

ファイルを扱う場合は、usingを使うのが安全だそうです。

また、try 〜 catchは例外処理を行う為のステートメントです。こちら(++C++; //未確認飛行C様)に説明があります。正しくは、try 〜 catch 〜 finallyで、

try {
  例外を発生する可能性があるコード
}
catch( 例外の種類 ) {
  例外処理をするコード
}
finally {
  例外の有無にかかわらず実行させたいコード
  リソースの開放などに使う
}

という書き方をします。

それで、上記のプログラムの例だと、usingを使ってファイル操作をしているので、finallyでリソースの開放をしなくても済んでいる、ということですね。

今回はこれで終わりにします。