C#

【C#】テキストファイルの入力パターンを把握しよう!【現場で役立つ】

こんにちは、C#エンジニアのエリーヴァです。
僕はフリーランスエンジニアとしてITのあらゆる現場のシステム開発に携わっています。

これまで色んな現場のソースを見てきて、どの現場も同じような実装するんだな~パターンに気づいたことがあるので、そのうちの1つを紹介します。
今回は「テキストファイルの入力」の実装パターンについてまとめていきます。

1行ずつ読み込むパターン

ファイルを読み込むためには、実装前に確認すべきことがあります。

  • System.IOの参照
  • 対象ファイルのパス

System.IOの名前空間は主に「ファイル操作」に関するクラスです。
これら用意されている様々なクラスを利用することでファイルを扱うことができます。

現場で実際に実装されているパターンは、以下のような考え方が多いです。

using System;
using System.Text;
using System.IO;

namespace TextReaderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var Path = @"C:\FileRead\sample.txt";
            //ファイルが存在しているか
            if (File.Exists(Path)) 
            {
                //UTF-8でファイルを読み込む
                using (var reader = new StreamReader(Path, Encoding.UTF8)) 
                {
                    //ファイルの最終行まで繰り返し処理
                    while (!reader.EndOfStream) 
                    {
                        //1行取得する
                        var line = reader.ReadLine();
                        //コンソール画面に取得行を表示
                        Console.WriteLine(line);
                    }
                }
            }
        }
    }
}

まず最初にFile.Existsメソッドより「ファイルが存在しているか」を確認します。
もし存在する場合のみ読み込み処理を実行します。

ファイルを読み込むには、以下の手順を踏みます。

  1. ファイルをオープンする
  2. 残りの行数を確認する
  3. 読み込んだ中身を格納する

サンプルソースでは、using (var reader = new StreamReader(Path, Encoding.UTF8)) の個所です。
StreamReaderクラスをインスタンス生成します。


StreamReaderクラスの中身をF12キーで確認すると引数や概要を確認できます。

// 概要:
//     文字エンコーディングを設定して、指定したファイル名用の System.IO.StreamReader クラスの新しいインスタンスを初期化します。
//
// パラメーター:
//   path:
//     読み込まれる完全なファイルパス。
//
//   encoding:
//     使用する文字エンコーディング。

ファイルのパスと文字エンコーディングを指定してファイルを開きますが、この際に第2引数(エンコーディング)は省略可能です。
もしも省略した場合は「UTF-8」となります。

もしも、unicodeやShift_JISで保存されている場合は、変更しましょう。

ひと昔は…(古いソースあるある)

try-finallyを使って実装しているパターンもあります。
しかし、ぶっちゃけイケてないです。

そもそもなぜ、usingを利用するのかというと「確実にリソースの解放」をするためです。
try-finallyでも可能ですが、ソースの読みやすさはusingのほうが圧倒的ですよね。

ちなみにこんな感じで書きます。

using System;
using System.Text;
using System.IO;

namespace TextReaderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var Path = @"C:\FileRead\sample.txt";
            StreamReader reader = new StreamReader(Path, Encoding.UTF8);
            try
            {
                //ファイルの最終行まで繰り返し処理
                while (!reader.EndOfStream)
                {
                    //1行取得する
                    var line = reader.ReadLine();
                    //コンソール画面に取得行を表示
                    Console.WriteLine(line);
                }
            }
            finally 
            {
                //リソースを解放
                reader.Dispose();
            }
        }
    }
}

やはり読みやすさを考えるとusingを使うほうが行も減りますし、分かりやすいですよね。

そーいえば…foreachは使えないのん?
reader.ReadLineより1行ずつ引っこ抜いてるからforeachは使えない…

もしもforeachを使う場合は一気に読み込む必要があります。

一気に読み込むパターン

ファイルの中身を一気に読み込むことでforeachによる処理が可能になります。
1行ずつ読み込む場合は、StreamReaderクラスのReadLineメソッドより1行取得していました。

ファイルを一気に読む場合はFileクラスのReadAllLinesメソッドを使う必要があります。
こちらよりstring型の配列に値を取得するので、foreachより1つずつ取得するという流れになります。

using System;
using System.Text;
using System.IO;

namespace TextReaderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var Path = @"C:\FileRead\sample.txt";
            var lines = File.ReadAllLines(Path, Encoding.UTF8);
            foreach (var line in lines) 
            {
                //コンソール画面に取得行を表示
                Console.WriteLine(line);
            }
        }
    }
}

注意すべきポイント

非常に分かりやすく便利なのですが、注意しなければならないところがあります。
次の場合は注意です。

  • 読み終わるまで処理待ちとなってしまう
  • メモリを圧迫してしまう

サイズの大きいテキストファイルの場合は、注意が必要ですのでサイズが小さいファイルの際に利用するのがよいでしょう。