Architect's Log

I'm a Cloud Architect. I'm highly motivated to reduce toils with driving DevOps.

DataTableの内容をCSVとして出力する拡張メソッドを定義する

DataTableの内容をCSVとして出力する - プログラマーな日々
DataTableの内容をCSVとして出力するサンプルです。

先日のエントリーと等価な処理を拡張メソッドで定義します。

DataTableExtension.cs

using System.Data;
using System.Text;

namespace Extensions {
    public static class DataTableExtension {
        /// <summary>
        /// DataTableの内容をCSVの文字列として取得します。
        /// </summary>
        /// <param name="self">DataTableのインスタンス。</param>
        /// <param name="delimiter">区切り文字。</param>
        /// <param name="addHeader">DataColumn名を連結した文字列をヘッダとして付加する場合はtrue。付加しない場合はfalse。</param>
        /// <param name="excludeDataColumns">対象外のカラムを示すDataColumnの配列。</param>
        /// <returns></returns>
        public static string ToCsv(
            this DataTable self, 
            string delimiter = ",", 
            bool addHeader = true, 
            params DataColumn[] excludeDataColumns) {
            StringBuilder rowData = new StringBuilder();

            if (addHeader) {
                foreach (DataColumn column in self.Columns) {
                    if (DataTableExtension.IsExcludeColumn(column, excludeDataColumns))
                        continue;

                    rowData.Append(column).Append(delimiter);
                }
                rowData.Length = rowData.Length - delimiter.Length;
                rowData.AppendLine();
            }

            foreach (DataRow row in self.Rows) {
                foreach (DataColumn column in self.Columns) {
                    if (DataTableExtension.IsExcludeColumn(column, excludeDataColumns))
                        continue;

                    if (row.IsNull(column)) {
                        rowData.Append(string.Empty).Append(delimiter);
                    } else {
                        if (column.DataType == typeof(string)) {
                            string s = ((string) row[column]).Replace("\"", "\"\"");
                            s = DataTableExtension.GetEnclosedDataInQuotesIfContainsDelimiterOrNewLineOrQuotes(s, delimiter);
                            rowData.Append(s).Append(delimiter);
                        } else {
                            rowData.Append(row[column]).Append(delimiter);
                        }
                    }
                }
                rowData.Length = rowData.Length - delimiter.Length;
                rowData.AppendLine();
            }
            return rowData.ToString();

        }

        private static bool IsExcludeColumn(DataColumn column, DataColumn[] excludeDataColumns) {
            foreach (DataColumn excludeCol in excludeDataColumns) {
                if (excludeCol == column)
                    return true;
            }
            return false;
        }

        private static string GetEnclosedDataInQuotesIfContainsDelimiterOrNewLineOrQuotes(string data, string delimiter) {
            if ((data.Contains(delimiter)) || (data.Contains("\n")) || (data.Contains("\""))) {
                return string.Format("\"{0}\"", data);
            } else {
                return data;
            }
        }
    }
}

Program.cs

using System;
using System.Data;
using Extensions;

namespace ConsoleApplication {
    class Program {
        static void Main(string[] args) {
            DataTable dt = new DataTable();
            dt.Columns.Add("col1", typeof(string));
            dt.Columns.Add("col2", typeof(string));
            dt.Columns.Add("col3", typeof(string));
            dt.Rows.Add("a", "b\nb", "c");
            dt.Rows.Add("1", "2,2", "3");
            dt.Rows.Add("あ", "い\"い", "う");
            Console.WriteLine(dt.ToCsv(excludeDataColumns : dt.Columns[2]));
            Console.ReadLine();
        }
    }
}

実行結果