質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.38%

  • C#

    9490questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • LINQ

    137questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

  • foreach

    83questions

    foreachは、List・Collection・Arrayといったデータ構造の各要素に対して繰り返し処理を実行するために扱われる、制御構造の構文です。

w(C#)ListデータのCSV(エクセル)への出力を高速化したい

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 455

widget11

score 153

コードの一例ではあるのですがDBから引っ張ってきた顧客等のデータをCSVに書き出したいとします。
現状はforeachで一件ずつエスケープ処理、ダブルクォーテーションのエスケープ処理、カンマのエスケープ処理等を行っている為、テーブルのフィールドやカラムが大量にあるとエクセルファイルへのエクスポートがとてつもなく時間がかかってしまいます。

  var sb = new StringBuilder();

            var columnList = customList.Columns.Select(column =>
            {
                if (shouldLocalizeHeader)
                {
                    return HtmlHelpers.LocalizedDisplayName(controllerName, column.DisplayName);
                }

                return column.DisplayName;
            });

            sb.AppendLine(string.Join(",", columnList));

            foreach (var item in customList.List)
            {
                var propertyList = customList.Columns.Select(column => ConvertCSVOutputFormat(item.GetType().GetProperty(column.Name).GetValue(item, null)));
                sb.AppendLine(string.Join(",", propertyList));
            }

            Encoding enc = Encoding.GetEncoding("shift_jis");
            using (var sw = new StreamWriter(fileName, false, enc))
            {
                sw.WriteLine(sb.ToString());
            }
        }
//エスケープ処理
public static string ConvertCSVOutputFormat(object value)
        {
            string csvValue = string.Empty;

            if (value != null)
            {
                csvValue = value.ToString();

                if (csvValue.Contains("\n") || csvValue.Contains(",") || csvValue.Contains("\""))
                {
                    csvValue = csvValue.Replace(Environment.NewLine, "\n")
                                       .Replace("\"", "\"\"");

                    csvValue = string.Format("\"{0}\"", csvValue);
                }
            }

            return csvValue;
        }


このforeachで一つのフィールドずつエスケープ処理を行っている箇所をまとめて処理したりして高速でエクスポートしたいのですがどのようなアプローチをとればよいのでしょうか?
宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • YAmaGNZ

    2019/01/08 18:07

    列数、行数と実際にかかる時間はどれくらいなのでしょうか?

    キャンセル

回答 4

+1

NuGetにあるライブラリを使うのも手です。CSVをNuGetで検索すると多数出てきます。
私自身は使ったことないのでこれ以上のコメントはできませんが、以下などはどうでしょうか?

C# で CSV を扱うのに CsvHelper を使う

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

実際に処理している部分のどこが遅いのでしょうか?
Stopwatch とかで時間を計ってみる事をお奨めします。

また、細かい話ですが、 stringクラスでの文字処理は遅いので、StringBuilderを使っていると思いますが、結構、string 処理してますね。
例えば、

sb.AppendLine(string.Join(",", propertyList));

string.Join() を使ったら、StringBuilder を使う価値が半減。
StringBuilder.Append() を使うようにした方が良いのでは?
等々、ありそうです。
ただ、まずは、どの部分が遅いのかの確認が先かと考えます。

[追記]
string.Join()は、遅くないとの指摘があったので、確認してみました。

    const int LoopCount = 100000000;
    Stopwatch sw = new Stopwatch();
    StringBuilder sb = new StringBuilder();
    string str1 = "abc";
    sw.Reset();
    sw.Start();
    for (int i = 0; i < LoopCount; i++) {
        sb.Clear();
        sb.AppendLine(string.Join(",", str1));
    }
    sw.Stop();
    Console.WriteLine("string.Join: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    for (int i = 0; i < LoopCount; i++) {
        sb.Clear();
        sb.Append(",");
        sb.AppendLine(str1);
    }
    sw.Stop();
    Console.WriteLine("StringBuilder.Append: " + sw.Elapsed);


この結果、手元の環境 (Win10)で、
string.Join(): 5.0 秒 (4.98 ~ 5.03)
StringBuilder.Append(): 1.9 秒 (1.91 ~ 1.92)
となりました。

環境、書き方の問題等はあると思いますが、参考までに。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/13 01:22

    計測するのは重要だと思いますが、
    string.joinsを使うな、間違いかな。
    https://referencesource.microsoft.com/#mscorlib/system/string.cs,fa961ce440526a4d
    StringBuilderを使って組み立てを行うので十分早いです。

    キャンセル

  • 2019/01/13 14:37

    一つの例で出したつもりですが、運が悪かったという事?
    確かに指摘のリンクによると、内部で、StringBuilderを使っているので、それほど遅くはない? ... (せいぜい、オーバーヘッド分?)
    時間取れたら、確認してみたいと思います。

    キャンセル

  • 2019/01/14 22:46

    string.Join()の件、計ってみました。
    まあ、他の方が指摘している問題の方が大きく、こちらはゴミかも知れませんが参考情報として。

    キャンセル

0

ぱっとみ、遅いのは、GetProperty().GetValue のところなのかな?
あれは、もともと遅くて有名なので。
なぜリフレクションは遅いのか

こういう感じでのを作って、予め関数をつくると早くなるのではないかな?
式木あたりでググってください。

    public static class Reflection
    {
        /// <summary>
        /// (type).GetProperty().GetValue の式木版
        /// </summary>
        /// <typeparam name="T">対象の型</typeparam>
        /// <typeparam name="T1">返す型</typeparam>
        /// <param name="property_name"></param>
        /// <returns></returns>
        public static Func<T, T1> GetValueFunc<T, T1>(string property_name)
        {
            var p = Expression.Parameter(typeof(T));
            var lambda = Expression.Lambda(Expression.Property(p, property_name), p);
            return (Func<T, T1>)lambda.Compile();
        }
    }

あと、ここは、存在確認だけなので、

   csvValue = value.ToString();
   if (csvValue.Contains("\n") || csvValue.Contains(",") || csvValue.Contains("\""))
   {
   }


↓とう言う感じで書いたほうが若干早いのかな?

            var csvValue = value.ToString();
            var hash = new HashSet<char>(csvValue.ToCharArray());
            if (hash.Contains('\n') || hash.Contains(',') || hash.Contains('\"'))
            {

            }


微々たるものだと思うけど。元の書き方だと、For文が3回実行するので。一回の走査で済むようにしたい。

なお、私は、こういう拡張メソッドを作って、Tsv出力している。
Csvは、面倒なだけなので、嫌いです。
GetProperties().GetValue なので、最速ではないのですが、それほど遅いというイメージはないのですけどねぇ・・。

        public static string ToTsv<T>(this IEnumerable<T> list)
        {
            System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
            stringBuilder.AppendLine(string.Join("\t", typeof(T).GetProperties().Select(n => n.Name).ToArray()));

            foreach (var item in list)
            {
                stringBuilder.AppendLine(string.Join("\t", typeof(T).GetProperties().Select(n => n.GetValue(item)?.ToString())));
            }
            return stringBuilder.ToString();
        }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

CSVHelperをNugetから使うのが妥当かと思います。

Nugetパッケージ使えないとかいうのであれば、理由が逆に知りたいです。

仕事で使っていて、なら尚更説得して、生産性重視でパッケージ使うべきかと思います。

CSVHelper であれば、使用しているので、質問をいただければ

少しはお役に立てるかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9490questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • LINQ

    137questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

  • foreach

    83questions

    foreachは、List・Collection・Arrayといったデータ構造の各要素に対して繰り返し処理を実行するために扱われる、制御構造の構文です。