Architect's Log

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

DataTableの内容をCSVとして出力する

DataTableの内容をCSVとして出力するサンプルです。

クラス

using System.Data;
using System.Text;

namespace Models {
    /// <summary>
    /// DataTableの内容ををフィールドとして読み込む機能を提供します。
    /// </summary>
    public class DataTableFieldReader {
        private DataTable dataTable;
        private string delimiter;
        private DataColumn[] excludeDataColumns;

        /// <summary>
        /// Initializes a new instance of the <see cref="DataTableFieldReader"/> class.
        /// </summary>
        /// <param name="dataTable">読み込むDataTable。</param>
        /// <param name="delimiter">区切り文字。</param>
        /// <param name="excludeDataColumns">
        /// 読み込み対象外のフィールドを示すDataColumnの配列。
        /// </param>
        public DataTableFieldReader(
            DataTable dataTable, 
            string delimiter, 
            params DataColumn[] excludeDataColumns) {
            this.dataTable = dataTable;
            this.delimiter = delimiter;
            this.excludeDataColumns = excludeDataColumns;
        }

        /// <summary>
        /// DataColumn名を区切り文字で連結した文字列を取得します。
        /// </summary>
        /// <returns>DataColumn名を区切り文字で連結した文字列。</returns>
        public string GetColumnNamesString() {
            StringBuilder columnNames = new StringBuilder();
            foreach(DataColumn column in dataTable.Columns) {
                if(this.IsExcludeColumn(column))
                    continue;

                columnNames.Append(column).Append(delimiter);
            }
            columnNames.Length = columnNames.Length - delimiter.Length;
            return columnNames.ToString();
        }

        /// <summary>
        /// DataRowの内容を区切り文字で連結した文字列を取得します。
        /// </summary>
        /// <returns>DataRowの内容を区切り文字で連結した文字列。</returns>
        /// <remarks>
        /// <para>DBNullは空白文字列で置き換えます。</para>
        /// <para>
        /// フィールドデータに区切り文字、改行、ダブルクォーテーションが含まれる場合は、
        /// ダブルクォーテーションで囲みます。
        /// </para>
        /// </remarks>
        public string GetRowDataString() {
            StringBuilder rowData = new StringBuilder();
            foreach(DataRow row in dataTable.Rows) {
                foreach(DataColumn column in dataTable.Columns) {
                    if(this.IsExcludeColumn(column))
                        continue;

                    if(row.IsNull(column)) {
                        rowData.Append(string.Empty).Append(delimiter);
                    } else {
                        if(column.DataType == typeof(string)) {
                            string s = ((string)row[column]).Replace("\"", "\"\"");
                            s = 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 bool IsExcludeColumn(DataColumn column) {
            foreach(DataColumn excludeCol in this.excludeDataColumns) {
                if(excludeCol == column)
                    return true;
            }
            return false;
        }

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

使い方

型指定されたデータテーブルを想定しています。

DataTableFieldReader reader
    = new DataTableFieldReader(dataTable, ",", dataTable.HogeColumn, dataTable.FugaColumn);
string data = reader.GetColumnNamesString() + Environment.NewLine + reader.GetRowDataString();