henna
@henna

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

.NET frameworkでDataTableからCSV出力がしたい

解決したいこと

Data Grid ViewのDatasourceとしてData Tableを用いています。
これをボタンクリックでCSV出力したいです。

発生している問題・エラー

CSV保存はできるのですが、中身が空で…。
ヘッダーすらできておりません。
フォイルサイズが0のままになってしまいます。
スクリーンショット (302).png

該当するソースコード

C#7.3 .Net Framework VisualStudio2022
ソースコードを入力
/// <summary>
        /// 買い物リストをcsvファイルとして保存する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <param name="csvPath"></param>
        /// <param name="writeHeader"></param>
        private void SaveButton_Click(object sender, EventArgs e, object csvPath, object writeHeader)
        {
            _ = dateTimePicker.Value.ToLongDateString();

            SaveFileDialog dlg = new SaveFileDialog
            {
                Title = "ファイルを保存する",
                FileName = "ItemList.csv",
                Filter = "CSV(カンマ区切り)|*.csv",
            };

            if (dlg.ShowDialog() == DialogResult.Cancel)
            {
                MessageBox.Show("キャンセルされました");
                return;
            }

            else
            {
                StreamWriter streamWriter = new System.IO.StreamWriter(dlg.FileName, false, Encoding.GetEncoding("Shift_JIS"));
                System.IO.StreamWriter writer = streamWriter;

                var colCount = dt.Columns.Count;
                int lastColIndex = colCount - 1;

                if ((bool)writeHeader)
                {
                    for (int i = 0; i < colCount; i++)
                    {
                         string field = dt.Columns[i].Caption;
                        field = EncloseDoubleQuotesIfNeed(field);
                        writer.Write(field);
                        if (lastColIndex > i)
                        {
                            writer.Write(',');
                        }
                    }

                    writer.Write("\r\n");
                }

                foreach (DataRow row in dt.Rows)
                {
                    for (int i = 0; i < colCount; i++)
                    {
                        string field = row[i].ToString();
                        field = EncloseDoubleQuotesIfNeed(field);
                        writer.Write(field);
                        
                        if (lastColIndex > i)
                        {
                            writer.Write(',');
                        }
                    }
                    //改行する
                    writer.Write("\r\n");
                }   
            }
        }

       
        /// <summary>
        /// 文字列をダブルクォートで囲む
        /// </summary>
        private string EncloseDoubleQuotesIfNeed(string field)
        {
            if (NeedEncloseDoubleQuotes(field))
            {
                return EncloseDoubleQuotes(field);
            }
            return field;
        }

        /// <summary>
        /// 文字列をダブルクォートで囲む
        /// </summary>
        private string EncloseDoubleQuotes(string field)
        {
            if (field.IndexOf('"') > -1)
            {
                //"を""とする
                field = field.Replace("\"", "\"\"");
            }
            return "\"" + field + "\"";
        }

        private bool NeedEncloseDoubleQuotes(string field)
        {
            return field.IndexOf('"') > -1 ||
                field.IndexOf(',') > -1 ||
                field.IndexOf('\r') > -1 ||
                field.IndexOf('\n') > -1 ||
                field.StartsWith(" ") ||
                field.StartsWith("\t") ||
                field.EndsWith(" ") ||
                field.EndsWith("\t");
        }

自分で試したこと

こちらを参照しました。

0

3Answer

まずはデバッグでステップ実行して、
・どの行まで正しく動作しているか
・変数やDataTableの中身は想定通りになっているか
原因の切り分けを行ってください。
必要最小限のコンソールアプリを作成し、自分で試した事にある参考先のページのソースをコピペしてそのまま呼び出しても本当にそうなりますか?

また、Webのサンプルで見かける独自CSV入出力はRFCに準拠していない簡易実装のものが多く、真面目に実装するならnugetでCSVHelperを導入するのがいいと思います。
下記は適当なサンプルですが、細かい使い方を確認するならCSVHelperでググれば沢山サンプルや解説ページがあるので調べてみてください。

using CsvHelper;
using System.Collections.Generic;
using System.Data;
using System.Dynamic;
using System.Globalization;
using System.IO;

class Program
{
    static void Main()
    {
        var table = new DataTable();
        table.Columns.Add("ID", typeof(int));
        table.Columns.Add("Value1");
        table.Columns.Add("Value2");
        table.Rows.Add(1, "a2", "a3");
        table.Rows.Add(2, "b2", "b3");

        table.WriteCSVFile(@"c:\test\test.csv");
    }
}

public static class MyDataTableExtentions
{
    public static void WriteCSVFile(this DataTable table, string filePath, bool hasHeader = true)
    {
        var config = new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture);
        config.HasHeaderRecord = hasHeader;

        using (var stream = new StreamWriter(filePath))
        using (var csv = new CsvWriter(stream, config))
        {
            if (table.Rows.Count > 0)
            {
                var records = new List<dynamic>();
                foreach (DataRow row in table.Rows)
                {
                    IDictionary<string, object> record = new ExpandoObject();
                    foreach (DataColumn column in table.Columns)
                    {
                        record.Add(column.ColumnName, row[column.Ordinal]);
                    }
                    records.Add(record);
                }
                csv.WriteRecords(records);
            }
            else if (hasHeader)
            {
                IDictionary<string, object> record = new ExpandoObject();
                foreach (DataColumn column in table.Columns)
                {
                    record.Add(column.ColumnName, null);
                }
                csv.WriteDynamicHeader((dynamic)record);
                csv.Flush();
                stream.WriteLine();
            }
        }
    }
}

/*
(test.csv出力結果)
ID,Value1,Value2
1,a2,a3
2,b2,b3
*/
2Like

開発環境を質問欄を編集して追記してください。(例: Windows 10 の Visual Studio 2022 で Windows Forms アプリをターゲットフレームワーク .NET Framework 4.8 で作っています・・・とか)

private void SaveButton_Click(object sender, EventArgs e, object csvPath, object writeHeader)

これはなんですか? .NET のイベントハンドラとしては引数が無効です。

以下の記事が参考になりませんか?

CSV ファイルを DataGridView に表示
http://surferonwww.info/BlogEngine/post/2020/09/11/show-date-in-csv-file-on-datagridview.aspx

0Like

StreamWriterのCloseを呼んでいないのが原因な気がします。
StreamWriterをnewしてる箇所をusingしてみてください。

0Like

Your answer might help someone💌