DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

DataTableや配列等をCSV形式のファイルとして保存する

CSV形式の規則は、「CSV形式のファイルをDataTableや配列等として取得する」で紹介したものと同じとして、話を進めます。つまり、4,6の規則に従い、フィールドにカンマ、改行文字が含まれる場合は、ダブルクォートで囲みます。また5の規則に従い、フィールドにダブルクォートが含まれる場合は、これを2つのダブルクォートに置換して、ダブルクォートで囲みます。さらに規則7に従い、フィールドの前後にスペースがある場合も、ダブルクォートで囲みます。

このような方針により、DataTableをCSV形式のファイルに保存する例(ConvertDataTableToCsvメソッド)を以下に示します。フィールドの型を考慮せず、単純にToStringメソッドで文字列にして保存しています。ヘッダも書き込んでいますが、ヘッダの書き込みと、レコードの書き込みが全く同じコードになっており、コードとしてはあまり良くありません。あくまでサンプルということで、ご了承ください。

またこの例では、いちいち一つ一つのフィールドを調べてダブルクォートで囲むか調べていますが、これが面倒であれば、すべてのフィールドをダブルクォートで囲むようにしても結構です。

VB.NET
コードを隠すコードを選択
''' <summary>
''' DataTableの内容をCSVファイルに保存する
''' </summary>
''' <param name="dt">CSVに変換するDataTable</param>
''' <param name="csvPath">保存先のCSVファイルのパス</param>
''' <param name="writeHeader">ヘッダを書き込む時はtrue。</param>
Public Sub ConvertDataTableToCsv( _
        dt As DataTable, csvPath As String, writeHeader As Boolean)
    'CSVファイルに書き込むときに使うEncoding
    Dim enc As System.Text.Encoding = _
        System.Text.Encoding.GetEncoding("Shift_JIS")

    '書き込むファイルを開く
    Dim sr As New System.IO.StreamWriter(csvPath, False, enc)

    Dim colCount As Integer = dt.Columns.Count
    Dim lastColIndex As Integer = colCount - 1
    Dim i As Integer

    'ヘッダを書き込む
    If writeHeader Then
        For i = 0 To colCount - 1
            'ヘッダの取得
            Dim field As String = dt.Columns(i).Caption
            '"で囲む
            field = EncloseDoubleQuotesIfNeed(field)
            'フィールドを書き込む
            sr.Write(field)
            'カンマを書き込む
            If lastColIndex > i Then
                sr.Write(","c)
            End If
        Next
        '改行する
        sr.Write(vbCrLf)
    End If

    'レコードを書き込む
    Dim row As DataRow
    For Each row In dt.Rows
        For i = 0 To colCount - 1
            'フィールドの取得
            Dim field As String = row(i).ToString()
            '"で囲む
            field = EncloseDoubleQuotesIfNeed(field)
            'フィールドを書き込む
            sr.Write(field)
            'カンマを書き込む
            If lastColIndex > i Then
                sr.Write(","c)
            End If
        Next
        '改行する
        sr.Write(vbCrLf)
    Next

    '閉じる
    sr.Close()
End Sub

''' <summary>
''' 必要ならば、文字列をダブルクォートで囲む
''' </summary>
Private Function EncloseDoubleQuotesIfNeed(field As String) As String
    If NeedEncloseDoubleQuotes(field) Then
        Return EncloseDoubleQuotes(field)
    End If
    Return field
End Function

''' <summary>
''' 文字列をダブルクォートで囲む
''' </summary>
Private Function EncloseDoubleQuotes(field As String) As String
    If field.IndexOf(""""c) > -1 Then
        '"を""とする
        field = field.Replace("""", """""")
    End If
    Return """" & field & """"
End Function

''' <summary>
''' 文字列をダブルクォートで囲む必要があるか調べる
''' </summary>
Private Function NeedEncloseDoubleQuotes(field As String) As Boolean
    Return field.IndexOf(""""c) > -1 OrElse _
        field.IndexOf(","c) > -1 OrElse _
        field.IndexOf(ControlChars.Cr) > -1 OrElse _
        field.IndexOf(ControlChars.Lf) > -1 OrElse _
        field.StartsWith(" ") OrElse _
        field.StartsWith(vbTab) OrElse _
        field.EndsWith(" ") OrElse _
        field.EndsWith(vbTab)
End Function
C#
コードを隠すコードを選択
/// <summary>
/// DataTableの内容をCSVファイルに保存する
/// </summary>
/// <param name="dt">CSVに変換するDataTable</param>
/// <param name="csvPath">保存先のCSVファイルのパス</param>
/// <param name="writeHeader">ヘッダを書き込む時はtrue。</param>
public void ConvertDataTableToCsv(
    DataTable dt, string csvPath, bool writeHeader)
{
    //CSVファイルに書き込むときに使うEncoding
    System.Text.Encoding enc =
        System.Text.Encoding.GetEncoding("Shift_JIS");

    //書き込むファイルを開く
    System.IO.StreamWriter sr =
        new System.IO.StreamWriter(csvPath, false, enc);

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

    //ヘッダを書き込む
    if (writeHeader)
    {
        for (int i = 0; i < colCount; i++)
        {
            //ヘッダの取得
            string field = dt.Columns[i].Caption;
            //"で囲む
            field = EncloseDoubleQuotesIfNeed(field);
            //フィールドを書き込む
            sr.Write(field);
            //カンマを書き込む
            if (lastColIndex > i)
            {
                sr.Write(',');
            }
        }
        //改行する
        sr.Write("\r\n");
    }

    //レコードを書き込む
    foreach (DataRow row in dt.Rows)
    {
        for (int i = 0; i < colCount; i++)
        {
            //フィールドの取得
            string field = row[i].ToString();
            //"で囲む
            field = EncloseDoubleQuotesIfNeed(field);
            //フィールドを書き込む
            sr.Write(field);
            //カンマを書き込む
            if (lastColIndex > i)
            {
                sr.Write(',');
            }
        }
        //改行する
        sr.Write("\r\n");
    }

    //閉じる
    sr.Close();
}

/// <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 + "\"";
}

/// <summary>
/// 文字列をダブルクォートで囲む必要があるか調べる
/// </summary>
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");
}

XSLTを使う方法

このような方法以外に、XSLTを使ってCSVに変換する方法もあります。この方法については、掲示板の過去ログの「DatasetのデータをCSV保存する」や、次のリンク先をご覧ください。

私個人の意見としましては、このような方法ではCSVの規則に従うファイルを出力するのが難しく、柔軟性にも乏しいため、お勧めはできません。

  • 履歴:
  • 2013/7/7 サンプルコードをメソッドにした。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。