自由設定のCSVファイル出力

他の業務システムに引き渡す目的でCSV形式ファイルをExcelで作成していませんか?
システム間の受け渡し用のCSV形式ファイルは仕様が単一ではありません。 システム間の受け渡しを行なうためのCSV形式ファイルを作成する側であったとして、単にExcel上に項目を並べて、 「名前を付けて保存」で「CSV (カンマ区切り) (*.csv)」の形式で保存させると、拡張子が「*.CSV」になったファイルは作成されますが、 インタフェースの仕様に合った形式や項目数で保存されるとは限らないということを知っておく必要があります。
どんな問題があるのでしょうか。
CSV形式ファイル」とか「CSV形式テキストファイル」と一般に言いますが、 この「CSV」とは「Comma Separated Values」の略であり、実体はただのテキストファイルです。
つまり、原理的に明確なのは、各項目を文字列として並べて、項目の境界に「カンマ」を挟むということです。
通常、Excelの「名前を付けて保存」で「CSV (カンマ区切り) (*.csv)」の形式を選択して保存させたファイルを「メモ帳」で開くと分かりますが、まさしくその通り単純にセルの値をカンマで挟んで並んでいるだけのテキストデータになっているはずです。
しかし、ここにはいくつかの問題があります。
@
受け取り側の要求仕様で「文字列項目はダブルクォーテーションやシングルクォーテーションで囲う」というケースがよくある。
A
日付項目を「#」で囲うというケースもある。
B
項目の値の途中に「カンマ」があると受け取り側で項目ズレを起こす。
C
この保存方法だと、17行目以降のカンマ数がズレることがある。
といった問題です。「カンマが項目境界」の原則以外が一般に「野放図」になっているためこのような問題があるのか、あるいは細かい「仕様」は受け渡しのシステム間で取り決めれば良いということかも知れませんが、 実際は、この「取り決め」を行なわないか理解ないままに運用を初めてトラブルになることも多いのではないでしょうか。

まずは、簡単な「CSV形式ファイル」を作成して確認してみましょう。ここにはB列(第2列)は文字列中に「カンマ」を入れた行も作ってあります。
CSV形式ファイルを作るためのExcelブック
こんなような単純なものにしておきます。

次に、これを「名前を付けて保存」で「CSV (カンマ区切り) (*.csv)」の形式で保存します。
名前を付けて保存で「CSV (カンマ区切り) (*.csv)」を選択
これで、「CSV形式ファイル」に保存されました。

では、「メモ帳」で開いてみましょう。
名前を付けて保存で「CSV (カンマ区切り) (*.csv)」を選択
Excelから「名前を付けて保存」で出力される「CSV形式ファイル」はこの形式にしかならないので、受け取り側の仕組みがこれに合っていれば問題はありません。
ですが、上記に挙げた問題点などが現に存在するので、そのようなケースに遭遇されたなら、ここにある方法を試してみて下さい。

※但し、「CSV形式ファイル」に出力するのが分かっていて、項目内に「カンマ」があるのを許可するのはそもそも考え物です。 「名前を付けて保存」では、項目内に「カンマ」が存在する場合のみ、その項目をダブルクォーテーョンで囲ってしまいますから、受け取り側がダブルクォーテーョンを想定していなかったり、 無条件にカンマだけで分解するような方法で受け取る仕組みだと、意図の通り受け取られないからです。

では、まず、そのソースコードです。
「組み込み用モジュール」ですが、出力させる「CSV形式ファイル」の仕様を決める部分はこのソースコードの先頭の定数の調整で行なうようになっています。 コード内の「↓↓↓ CSV形式ファイルの出力方法の指定 ↓↓↓」と書かれた範囲にある各定数の値を調整することで、所望の仕様にすることができるようになっています。 これらの値の調整で問題なく利用できれば、プロシージャ自身の調整なく利用できると思います。
逆にソースコードの内容を理解しようというのであれば、このコードの母体となっているのは「CSV形式テキストデータの書き出し」ですから、こちらを参照してみて下さい。

'*******************************************************************************
'   CSV形式テキストファイル書き出すサンプル(FSO)    ※汎用型
'
'   作成者:井上治  URL:http://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'*******************************************************************************
' [参照設定]
'   ・Microsoft Scripting Runtime
'*******************************************************************************
Option Explicit
'-------------------------------------------------------------------------------
' ※この下の各項目を設定することで、ソースコードを改変しなくても
'  いろいろなケースのCSV形式ファイル出力に対応できると思います。
'-------------------------------------------------------------------------------
'            ↓↓↓ CSV形式ファイルの出力方法の指定 ↓↓↓
'-------------------------------------------------------------------------------
' ■出力ファイルに関する指定
Private Const g_cnsDEFFILE = "SAMPLE.csv"   ' ファイル名(デフォルト)
Private Const g_cnsGETFILE = 1              ' ファイル名可変指定(0=固定、1=可変)
' ※「0=固定」の場合はこのブックのフォルダに出力
' ※「1=可変」の場合は「名前を付けて保存」のダイアログが表示される

' ■シート上の出力範囲の指定
Private Const g_cnsSTRGYO = 2               ' 出力開始行
Private Const g_cnsCHKCOL = 1               ' 最終行判定カラム番号
Private Const g_cnsSTRCOL = 1               ' 出力開始カラム番号
Private Const g_cnsENDCOL = 5               ' 出力終了カラム番号
Private Const g_cnsMIDASHI = 0              ' 先頭行見出し指定
' ※(先頭行見出し指定の値の説明)
'  0=見出しなし(先頭行からデータとして編集)
'  1=先頭行のみセパレータのみ(下記「カラム単位の編集」は無視)
'  2=先頭行は全て「"」囲い(下記「カラム単位の編集」は無視)
'  3=先頭行は全て「'」囲い(下記「カラム単位の編集」は無視)

' ■CSV形式ファイルのセパレータの指定(通常はカンマ)
Private Const g_cnsSEPCHR = ","             ' セパレータ文字
'Private Const g_cnsSEPCHR = vbTab           ' Tab区切りはこちらを有効にする

' ■カラム単位の編集方法の指定
'                カラム位置⇒ ....*....1....*....2....*....3....*....4....*....5
Private Const g_cnsCOLEDIT = "29910000000000000000000000000000000000000000000000"
' ※(カラム単位の編集方法の値の説明)
'  0=セパレータのみ
'  1=数値(ブランクはゼロ出力)
'  2=文字列(「"」囲い:無条件)
'  3=文字列(「"」囲い:ブランクを除く)
'  4=文字列(「'」囲い:無条件)
'  5=文字列(「'」囲い:ブランクを除く)
'  6=日付(「#」囲い:ブランクを除く)
'  8=自動判定(数値,ブランク以外は「"」囲い)
'  9=自動判定(数値,ブランク以外は、日付は「#」囲い、その他は「"」囲い)
'-------------------------------------------------------------------------------
'            ↑↑↑ CSV形式ファイルの出力方法の指定 ↑↑↑
'-------------------------------------------------------------------------------
Private Const g_cnsDQ = """"
Private Const g_cnsSQ = "'"
Private Const g_cnsSH = "#"
Private Const g_cnsYEN = "\"

'*******************************************************************************
' CSV形式テキストファイル書き出すサンプルC(FSO)  ※汎用型
'-------------------------------------------------------------------------------
'   ※シートの指定がない場合はアクティブなシートが対象になります。
'   ※データの条理的なチェックは行ないません。
'   ※処理内では実行時エラーの対応はしていません。
'*******************************************************************************
Sub WRITE_CSVFile4(Optional SH As Worksheet)
    Dim FSO As New FileSystemObject ' FileSystemObject
    Dim TS As TextStream            ' TextStream
    Dim strFILENAME As String       ' 出力ファイル名
    Dim vntFILENAME As Variant      ' 出力ファイル名指定
    Dim GYO As Long                 ' 収容するセルの行
    Dim GYOMAX As Long              ' データが収容された最終行
    Dim strREC As String            ' 出力レコード(見出し編集用)
    Dim COL As Long                 ' カラム(見出し編集用)
    Dim strDC As String             ' 囲い文字(見出し編集用)

    ' 出力ファイル名の指定
    If g_cnsGETFILE = 1 Then
        ' ファイル名をダイアログで指定
        vntFILENAME = Application.GetSaveAsFilename(g_cnsDEFFILE)
        If VarType(vntFILENAME) = vbBoolean Then Exit Sub
        strFILENAME = vntFILENAME
    Else
        ' ファイル名は初期値固定(自フォルダ)
        strFILENAME = ThisWorkbook.Path & g_cnsYEN & g_cnsDEFFILE
    End If
    ' ワークシート判定(Nothingの場合はActiveSheet)
    If SH Is Nothing Then Set SH = ActiveSheet
    ' 最終行の取得
    GYOMAX = SH.Cells(65536, g_cnsCHKCOL).End(xlUp).Row
    ' 指定ファイルをOPEN(出力モード、既存は無条件上書き)
    Set TS = FSO.CreateTextFile(Filename:=strFILENAME, Overwrite:=True)
    ' 指定行目から開始
    GYO = g_cnsSTRGYO
    ' 見出し処理
    If g_cnsMIDASHI <> 0 Then
        ' 見出し指定がある場合は見出しのみ別指定方法で編集
        Select Case g_cnsMIDASHI
            Case 1: strDC = ""
            Case 2: strDC = g_cnsDQ
            Case 3: strDC = g_cnsSQ
        End Select
        strREC = strDC & SH.Cells(GYO, g_cnsSTRCOL).Value & strDC
        COL = g_cnsSTRCOL + 1
        Do While COL <= g_cnsENDCOL
            strREC = strREC & g_cnsSEPCHR & _
                strDC & Trim(SH.Cells(GYO, COL).Value) & strDC
            COL = COL + 1
        Loop
        ' レコードを出力
        TS.WriteLine strREC
        GYO = GYO + 1
    End If
    ' 最終行まで繰り返す
    Do Until GYO > GYOMAX
        ' レコードを出力(REC編集処理より受け取る)
        TS.WriteLine FP_EDIT_CSVREC(SH, GYO)
        ' 行を加算
        GYO = GYO + 1
    Loop
    ' 指定ファイルをCLOSE
    TS.Close
    Set TS = Nothing
    Set FSO = Nothing
End Sub

'*******************************************************************************
' CSV形式テキストの1レコードの編集処理(引数はシート、シート上の行位置)
'*******************************************************************************
Private Function FP_EDIT_CSVREC(SH As Worksheet, GYO As Long) As String
    Dim strREC As String
    Dim COL As Long

    ' 先頭カラムの編集
    COL = g_cnsSTRCOL
    strREC = FP_EDIT_COLUMN(SH, GYO, COL)
    ' 2番目以降のカラムの編集
    Do While COL <= g_cnsENDCOL
        strREC = strREC & g_cnsSEPCHR & FP_EDIT_COLUMN(SH, GYO, COL)
    Loop
    ' 編集したレコード内容を戻り値にセット
    FP_EDIT_CSVREC = strREC
End Function

'*******************************************************************************
' 1カラム分の編集処理(引数はシート、行位置、カラム位置)
'*******************************************************************************
Private Function FP_EDIT_COLUMN(SH As Worksheet, GYO As Long, COL As Long) As String
    Dim strTEXT As String

    strTEXT = Trim(SH.Cells(GYO, COL).Value)
    Select Case Mid(g_cnsCOLEDIT, COL, 1)
        Case "0"                ' セパレータのみ
            FP_EDIT_COLUMN = strTEXT
        Case "1"                ' 数値(ブランクはゼロ出力)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = "0"
            Else
                FP_EDIT_COLUMN = strTEXT
            End If
        Case "2"                ' 文字列(「"」囲い:無条件)
            FP_EDIT_COLUMN = g_cnsDQ & strTEXT & g_cnsDQ
        Case "3"                ' 文字列(「"」囲い:ブランクを除く)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = strTEXT
            Else
                FP_EDIT_COLUMN = g_cnsDQ & strTEXT & g_cnsDQ
            End If
        Case "4"                ' 文字列(「'」囲い:無条件)
            FP_EDIT_COLUMN = g_cnsSQ & strTEXT & g_cnsSQ
        Case "5"                ' 文字列(「'」囲い:ブランクを除く)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = strTEXT
            Else
                FP_EDIT_COLUMN = g_cnsSQ & strTEXT & g_cnsSQ
            End If
        Case "6"                ' 日付(「#」囲い:ブランクを除く)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = strTEXT
            Else
                FP_EDIT_COLUMN = g_cnsSH & strTEXT & g_cnsSH
            End If
        Case "8"
            ' 自動判定(数値,ブランク以外は「"」囲い)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = strTEXT
            ElseIf IsNumeric(strTEXT) = True Then
                FP_EDIT_COLUMN = CStr(CDbl(strTEXT))            ' 数値
            Else
                FP_EDIT_COLUMN = g_cnsDQ & strTEXT & g_cnsDQ    ' その他(文字列)
            End If
        Case Else
            ' 自動判定(数値,ブランク以外は、日付は「#」、その他は「"」囲い)
            If strTEXT = "" Then
                FP_EDIT_COLUMN = strTEXT
            ElseIf IsDate(strTEXT) = True Then
                FP_EDIT_COLUMN = g_cnsSH & strTEXT & g_cnsSH    ' 日付
            ElseIf IsNumeric(strTEXT) = True Then
                FP_EDIT_COLUMN = CStr(CDbl(strTEXT))            ' 数値
            Else
                FP_EDIT_COLUMN = g_cnsDQ & strTEXT & g_cnsDQ    ' その他(文字列)
            End If
    End Select
    ' カラムを加算
    COL = COL + 1
End Function

'*******************************************************************************
' テスト用起動プロシージャ
'*******************************************************************************
Sub WRITE_CSV_TEST()
    If g_cnsGETFILE <> 1 Then
        If MsgBox("CSV形式ファイルへの出力を行ないます。" & vbCr & _
            "よろしいですね。", vbInformation + vbYesNo) <> vbYes Then Exit Sub
    End If
    Call WRITE_CSVFile4
End Sub

'-----------------------------<< End of Source >>-------------------------------
ご覧のように、やや長いソースコードになりましたが、 では、「定数」の各項目の説明です。
項 目 内 容
g_cnsDEFFILE デフォルトの出力ファイル名です。次の「g_cnsGETFILE」が「0(固定)」の場合はこのファイル名で出力されます。
g_cnsGETFILE 「名前を付けて保存」のダイアログで出力ファイル名を指定するかどうかの区分です。
   0=[固定]「g_cnsDEFFILE」のファイル名で、本ブックと同じフォルダに出力されます。
   1=[可変]「名前を付けて保存」のダイアログで任意にファイル名と保存場所を指定できます。
g_cnsSTRGYO シート上の出力開始行番号を指定します。シート上には「見出し」があるけれど、CSV形式ファイルには「見出し」は出力しないというなら、データの先頭行の行番号を指定します。
g_cnsCHKCOL データの最終行を判定する列(カラム)番号を指定します。最終行の判定は65536行からCtrl+↑を押すのと同じ方法で行なうので、入力が必須となっている列(カラム)番号を指定して下さい。
通常は列(カラム)は番号ではなくアルファベットとなっているので、番号の判断が難しいですが、表示形式をR1C1参照形式」に変更すると列(カラム)方向も数字になるので、この時に行列番号に表示されている番号を指定します。
g_cnsSTRCOL CSV形式ファイルに出力させる最初の列(カラム)番号を指定します。A列から出力させるなら「1」となります。
g_cnsENDCOL CSV形式ファイルに出力させる最後の列(カラム)番号を指定します。例えば、E列まで出力させるなら「5」となります。
g_cnsMIDASHI 先ほどの「出力開始行番号(g_cnsSTRGYO)」の行が「見出し」なのかどうかを指定します。
   0=見出しなし(先頭行からデータとして下記「カラム単位の編集」に従って編集)
   1=先頭行のみセパレータのみ(下記「カラム単位の編集」は無視)
   2=先頭行は全てダブルクォーテーョン(")囲い(下記「カラム単位の編集」は無視)
   3=先頭行は全てシングルクォーテーョン(')囲い(下記「カラム単位の編集」は無視)
g_cnsSEPCHR CSV形式ファイルの項目間のセパレータとなる文字で、通常は「カンマ(,)」ですが、これを「vbTab」に変更することによりタブ区切りテキストファイルを出力させることもできます。
g_cnsCOLEDIT 最後は、列(カラム)ごとの編集方法の指定です。デフォルトでは「2991000000...」と適当な数字が並んでいますが、最初の「2」がA列の編集指定、次の「9」がB列の編集指定、 というように1桁が1つの列(カラム)に対応した編集方法の指定となります。とりあえず50カラム分になっていますが、出力させる列がこれより多い場合は必要桁数分に広げて下さい。 実際には「最初の列(カラム)番号(g_cnsSTRCOL)」「最後の列(カラム)番号(g_cnsENDCOL)」の範囲以外の文字は何の作用もしません。 また、「最初の列(カラム)番号(g_cnsSTRCOL)」が「1」でない場合は、その番号の左側に桁位置を合わせるためのダミーとなる何らかの文字を並べておく必要があります。
   0=項目セパレータ(カンマ)のみで、文字列境界には何も出力しません。
   1=文字列境界には何も出力しませんが、数値項目用にセルがブランクの場合はゼロ(0)を1つ出力します。
   2=その項目がブランクでも全てダブルクォーテーション(")で囲います。
   3=その項目がブランクの場合を除いて、ダブルクォーテーション(")で囲います。
   4=その項目がブランクでも全てシングルクォーテーション(')で囲います。
   5=その項目がブランクの場合を除いて、シングルクォーテーション(')で囲います。
   6=その項目がブランクの場合を除いて、シャープ(#)で囲います。(VB系日付項目用)
   8=その項目が数値かブランクの場合を除いて、ダブルクォーテーション(")で囲います。
   9=その項目が数値かブランクの場合は囲い文字なしで、日付の場合はシャープ(#)で囲い、それ以外はダブルクォーテーション(")で囲います。

最後に組み込みと起動方法のご注意です。
下記の「ダウンロード」ボタンでLHA自動解凍書庫ファイルがダウンロードできます。クリックしたら「保存」を選択して下さい。 保存させたら、WriteCSVFile4.exeを起動させて解凍するか、解凍ツールで解凍させて下さい。
解凍させるとWriteCSVFile4.xlsmodWRITE_CSVFile4.basの2つのファイルが作成されます。 WriteCSVFile4.xlsmodWRITE_CSVFile4.basを組み込んだサンプルで、modWRITE_CSVFile4.basが上記のコード本体です。
利用するExcelブックを開いて、VisualBasicEditorを開き、プロジェクトエクスプローラでそのExcelブックのプロジェクトを右クリックさせ、「ファイルのインポート」を選択して、 modWRITE_CSVFile4.basをインポートさせて下さい。
続いて、「Microsoft Scripting Runtime」を参照設定でチェックして下さい。
ここまでの作業ができたら、前項の表に従って出力項目の設定を行なって、準備完了です。
VisualBasicEditorを閉じて起動できますが、予めモジュールを組み込んだ状態を保存させておいた方が良いでしょう。 マクロの起動にWRITE_CSV_TESTがあるので、現在アクティブなシートが出力対象ならそのまま出力可能です。 出力を行なったら、出力されたCSV形式ファイルを「メモ帳」かテキストエディタで開いて、編集出力状態を確認して下さい。 ダブルクリックしてExcelで開いても確認にはなりません。Excel上ではCSV形式ファイル上に「"0001"」として出力されている項目が 数字の「1」だけで表示されてしまうので注意して下さい。
現在アクティブなシートが出力対象ではない場合は、そのシートのオブジェクトを引数にして、WRITE_CSVFile4を呼びだす上位プロシージャを作成すれば出力可能です。
いかがでしょう、CSV形式ファイルの出力で問題を抱えている場合、ほとんどのケースはこのようなマクロを利用すれば解決できると思います。

ダウンロードはこちら。
←WriteCSVFile4.zip
      (26KB)