developer's diary

最近はc#のエントリが多いです

【解答】C# (dotnetcore) CSVファイルを永続化に利用したコンソールアプリの作成

本エントリは、以下のエントリに対する解答の1つです。

mitsugeek.net

解答(ソース)

以下が回答となります。このプログラムには、改善ポイント(課題)があります。

using System;
using System.Collections.Generic;

namespace csvExample
{
    class Program
    {
        //メモリ上の住所録データ
        private static List<string[]> Rows = new List<string[]>();

        //CSVファイルのパス
        private const string CSV_PATH = @"data.csv";

        static void Main(string[] args)
        {
            //CSVファイルを読み込む
            ReadCsv();

            while (true) {
                //コマンドの一覧を表示
                Console.WriteLine("Command:list or add or init or exit");

                //コマンドの入力を求める
                string command = Console.ReadLine();

                if(command == "list")
                {
                    //一覧表示の場合
                    foreach(var row in Rows)
                    {
                        //メモリ上の内容を表示
                        Console.Write("name:[");
                        Console.Write(row[0]);
                        Console.Write("] telno:[");
                        Console.Write(row[1]);
                        Console.WriteLine("]");
                    }
                }
                else if(command == "add")
                {
                    //追加の場合

                    //追加する内容の読み込み
                    Console.WriteLine("name?");
                    string name = Console.ReadLine();
                    Console.WriteLine("telno?");
                    string telno = Console.ReadLine();

                    //メモリに追加
                    Rows.Add(new string[] { name, telno });

                    //ファイルに書き込む
                    WriteCsv();

                }
                else if(command == "init")
                {
                    //初期化の場合
                    if (System.IO.File.Exists(CSV_PATH))
                    {
                        System.IO.File.Delete(CSV_PATH);
                        Rows = new List<string[]>();
                    }
                }
                else if (command == "exit")
                {
                    //終了の場合
                    break;
                }
            }
            

        }

        //CSV書き込み
        static void WriteCsv()
        {
            //ファイルを書き込む
            using (var writer = new System.IO.StreamWriter(CSV_PATH))
            {
                //現在の内容を回す
                foreach (var row in Rows)
                {
                    //1行ずつ書き込む(項目をカンマ区切りでつなげる)
                    writer.WriteLine(String.Join(",", row));
                }
            }
        }

        //CSV読み込み
        static void ReadCsv()
        {
            //ファイルが無い場合は何もしない
            if (!System.IO.File.Exists(CSV_PATH)) return;

            //ファイルがある場合、読み込む
            using (var reader = new System.IO.StreamReader(CSV_PATH))
            {
                //1行ずつ読み込む
                string line;
                while((line = reader.ReadLine()) != null)
                {
                    //カンマ区切りを分解
                    Rows.Add(line.Split(","));
                }
            }
        }
    }
}

各項目の解説

ここでの項目は、プロパティやメソッドのことを表しています。 それぞれ、以下のような役割があります。

項目 説明
Rows CSVから読み込んだデータを保持する領域。データを追加した場合、ここに追加される
CSV_PATH CSVファイルのパス
Main プログラムのエントリポイント。
初期処理で、ReadCSVを実行し、
その後はlist、add、init、exitのコマンドを受け付け、各コマンドに合わせた処理を行っている
WriteCsv Rowsの内容をCSVファイルに保存する関数
ReadCsv CSVファイルを読み込み、内容をRowsに設定する関数

プログラム上の課題

カンマを含められない

nameや、telnoにカンマを含められません。 このプログラムでカンマを入力することは想定されていませんが、 できれば、CSVの標準的な保存、読み込みとしたいところです。

Main関数の行数が多い

もう少し分割したいところですね。

コマンド毎に関数に分けるっていうアプローチが良さそうですね。

Dictionary<string, Action >というアプローチもありかもしれません。

ファイルの行数に伴いレスポンスが悪化する

保存する毎に、ファイルを作り直しているため、レコード数が増えると遅くなっていく設計となっている。

追加しか行わないなら、追記というアプローチを行うべき。

ファイルの場合、固定長とすることで、部分編集、レコードの削除等も高速にできそう。

今風の作りにする場合は、データベースを利用するという事になる。

この場合はSQLiteが良さそう。

RDBMSが今風というのは少し違う気もするが・・・

BerkeleyDBとか・・・

発展のアプローチ

本プログラムの発展を考えたアプローチとして、以下のようなことができれば便利そう。

コマンドライン引数を使った操作ができれば便利かもしれない

コマンドライン引数で、list、add、initを操作できれば、以下のように、パイプやリダイレクトを利用した、他プログラムとの連携も可能となる。

dotnet csvExample.dll list > data.txt

改善エントリ追加しました。

mitsugeek.net