読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

Linq入門記-53 (補足, .NET 4.0で追加された遅延評価されるメソッド, System.IO.File.ReadLines)

C# Linq

今回も、ちょっとした補足事項です。


.NET 4.0になり、今まで存在していたメソッドの遅延評価版が追加されたクラスが
いくつかあります。そのうちの一つが

System.IO.File

クラスで、以下のメソッドが追加されています。

  • ReadLines


上記のメソッドは、従来から存在していた以下のメソッドの遅延評価版です。

  • ReadAllLines


書式は以下の通り。

public static IEnumerable<string> ReadLines(
	string path
)

public static IEnumerable<string> ReadLines(
	string path,
	Encoding encoding
)


前回のDirectory.EnumerateFilesと同様、戻り値がIEnumerableになっています。
遅延評価されるということは、LINQで処理しやすいということです。


今までだと、ReadAllLinesメソッドは内部で合致する情報のコレクションを
構築してから値を返してくれていました。つまり、内容を列挙する場合
完全にコレクションが完成するまで待つ必要がありました。


ReadLinesメソッドの場合は、コレクションが完成する前に列挙を始める事が
可能です。LINQを利用すると、whereで必要な情報のみに絞り込んだりするのも楽です。


以下、サンプルです。
このサンプルでは、最初に巨大なファイルを作成した後
それぞれのメソッドで列挙処理が開始されるまでの時間を計測して表示しています。

    #region LinqSamples-50
    public class LinqSamples50 : IExecutable
    {
        public void Execute()
        {
            //
            // File.ReadLinesメソッドは、従来までの
            // File.ReadAllLinesメソッドと同じ動作するメソッドである。
            //
            // 違いは、戻り値がIEnumerable<string>となっており
            // 遅延評価される。
            //
            // ReadAllLinesメソッドの場合は、全リストを構築してから
            // 戻り値が返却されるので、コレクションが構築されるまで
            // 待機する必要があるが、ReadLinesメソッドの場合は
            // コレクション全体が返される前に、列挙可能である。
            //
            Console.WriteLine("ファイル作成中・・・・");
            
            var tmpFilePath = CreateSampleFile(1000000);
            if (string.IsNullOrEmpty(tmpFilePath))
            {
                Console.WriteLine("ファイル作成中にエラー発生");
            }
            
            Console.WriteLine("ファイル作成完了");
            
            try
            {
                var watch   = Stopwatch.StartNew();
                var elapsed = string.Empty;
                
                var numberFormatInfo = new NumberFormatInfo { CurrencySymbol = "gsf_zero" };
                
                //
                // File.ReadAllLines
                //
                var query = from   line in File.ReadAllLines(tmpFilePath)
                            where  int.Parse(line, NumberStyles.AllowCurrencySymbol, numberFormatInfo) % 2 == 0
                            select line;
                
                foreach (var element in query)
                {
                    if (watch != null)
                    {
                        watch.Stop();
                        elapsed = watch.Elapsed.ToString();
                        watch = null;
                    }
                    
                    //Console.WriteLine(element);
                }
                
                Console.WriteLine("================== ReadAllLines          : {0} ==================", elapsed);
                
                //
                // File.ReadLines
                //
                watch   = Stopwatch.StartNew();
                elapsed = string.Empty;
                
                query = from   line in File.ReadLines(tmpFilePath)
                        where  int.Parse(line, NumberStyles.AllowCurrencySymbol, numberFormatInfo) % 2 == 0
                        select line;
                
                foreach (var element in query)
                {
                    if (watch != null)
                    {
                        watch.Stop();
                        elapsed = watch.Elapsed.ToString();
                        watch = null;
                    }
                    
                    //Console.WriteLine(element);
                }
                
                Console.WriteLine("================== ReadLines             : {0} ==================", elapsed);
            }
            finally
            {
                if (File.Exists(tmpFilePath))
                {
                    File.Delete(tmpFilePath);
                }
            }
        }
        
        string CreateSampleFile(int lineCount)
        {
            var tmpFileName = Path.GetTempFileName();
            
            try
            {
                //
                // 巨大なファイルを作成する.
                //
                using (var writer = new StreamWriter(new BufferedStream(File.OpenWrite(tmpFileName))))
                {
                    for (int i = 0; i < lineCount; i++)
                    {
                        writer.WriteLine(string.Format("gsf_zero{0}", i));
                    }
                    
                    writer.Flush();
                    writer.Close();
                }
            }
            catch
            {
                if (File.Exists(tmpFileName))
                {
                    File.Delete(tmpFileName);
                }
                
                return string.Empty;
            }

            return tmpFileName;
        }
    }
    #endregion


実行結果は以下の通りです。

  ファイル作成中・・・・
  ファイル作成完了
  ================== ReadAllLines          : 00:00:00.4391589 ==================
  ================== ReadLines             : 00:00:00.0008055 ==================


また、Fileクラスにはこの他にも

  • AppendAllLines
  • WriteAllLines

メソッドが追加されています。どちらもIEnumerable(Of String)を
受け取るようになっています。



================================
過去の記事については、以下のページからご参照下さい。