みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
引き続きエクセルVBAで様々なタイプのCSVを取り込んでいきます。
前回はCSVのデータがダブルクォーテーションで囲まれているパターンのCSVを取り扱いました。
今回は、データの中にカンマが含まれている場合のCSVをエクセルVBAで取り込む方法についてお伝えしていきます。
では、行ってみます!
前回のおさらいと今回の課題
まず、前回のおさらいをしていきます。
'CSVファイルの取り込み 一行ずつ取得しカンマでスプリットする最も定番なパターン
Sub getCSV()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets(1)
Dim strPath As String
strPath = "C:UsersNoriakiDropbox40_ブログvba-csvtestラーメン店アンケート_dq.csv"
Dim i, j As Long
Dim strLine As String
Dim arrLine As Variant 'カンマでsplitして格納
Open strPath For Input As #1 'csvファイルをオープン
i = 1
Do Until EOF(1)
Line Input #1, strLine
arrLine = Split(Replace(strLine, """", ""), ",") 'strLineをカンマで区切りarrLineに格納
For j = 0 To UBound(arrLine)
ws.Cells(i, j + 1).Value = arrLine(j)
Next j
i = i + 1
Loop
Close #1
End Sub
Line InputでCSVのレコードを一行取り込むと、ダブルクォーテーションをReplaceで削除した上で、Splitでカンマ区切りをするという処理ですね。
データにカンマが含まれるとそこで分割されてしまう
今回は、取り扱うCSVデータを少し改良しまして、このようなファイルを作成しました。
チャーシュー麺の定価が1,000円ですので、CSVデータ内には「1,000円」とカンマが含まれたデータが含まれています。
カンマ…このままでちゃんと処理できるのでしょうか?
では、これを上記プログラムで取り込んでみます。
やっぱり駄目でした…
チャーシューの行のセルがズレてしまっています。
本当は分割してほしくないのですが「1,000」のカンマでSplitされてしまっているんです。
カンマがデータに含まれているCSVデータを取り込むプログラム
では、データの中にカンマが含まれている場合のCSV取込み方を紹介していきます。
プログラムはこちらです!
'CSVファイルの取り込み データ内にカンマが含まれているパターン
Sub getCSV_camma()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets(1)
Dim strPath As String
strPath = "C:UsersNoriakiDropbox40_ブログvba-csvtestラーメン店アンケート_dq & comma.csv"
Dim i, j As Long
Dim strLine As String
Dim arrLine As Variant 'カンマでsplitして格納
Open strPath For Input As #1 'csvファイルをオープン
i = 1
Do Until EOF(1)
Line Input #1, strLine
arrLine = Split(Replace(replaceColon(strLine), """", ""), ":") 'strLineをコロンで区切りarrLineに格納
For j = 0 To UBound(arrLine)
ws.Cells(i, j + 1).Value = arrLine(j)
Next j
i = i + 1
Loop
Close #1
End Sub
前回のプログラムから変わったのは21行目だけです。
Splitがカンマではなくて「:」つまりコロンになっていますね。
またstrLineを引数としたreplaceColon…謎の関数があります。
これは自作の関数です。以降で解説をしていきます。
ダブルクォーテーションの外のカンマをコロンに置き換える関数
replaceColonは文字列を引数strとして受け取り、とある変更を加えて戻す関数です。
とある変更、というのは
- テータに含まれないカンマはコロンに置き換える
- データに含まれるカンマはそのまま
という処理です。
こうすることで、返された文字列はコロンでSplitをすればよい、ということになります。
プログラムはこちらです。
'受け取った文字列のカンマをコロンに置き換える
'ダブルクォーテーションで囲まれているカンマは置き換えない
Function replaceColon(ByVal str As String) As String
Dim strTemp As String
Dim quotCount As Long
Dim l As Long
For l = 1 To Len(str) 'strの長さだけ繰り返す
strTemp = Mid(str, l, 1) 'strから現在の1文字を切り出す
If strTemp = """" Then 'strTempがダブルクォーテーションなら
quotCount = quotCount + 1 'ダブルクォーテーションのカウントを1増やす
ElseIf strTemp = "," Then 'strTempがカンマなら
If quotCount Mod 2 = 0 Then 'quotCountが2の倍数なら
str = Left(str, l - 1) & ":" & Right(str, Len(str) - l) '現在の1文字をコロンに置き換える
End If
End If
Next l
replaceColon = str
End Function
関数replaceColon(ByVal str As String)の流れを整理してみますね。
For文とMidで文字列の最初から1文字切り出す
まずFor文で受け取った文字列のstrの文字を最初からなめていきます。
11行目のMidでstrから現在の文字を1文字取り出してstrTempに格納しています。
Midは
Mid(対象文字列,開始文字数,切り出す文字数)
と記述して、対象文字列を最初からの文字数から切り出す文字数分を切り抜いて返します。
ダブルクォーテーションの数をカウントして区切るべきカンマを見定める
次に、strTempがダブルクォーテーションならquotCountが1プラスされます。quotCountは今まで出会ったダブルクォーテーションの数をカウントしていますので
- quotCountが偶数:strTempはデータの外
- quotCountが奇数:strTempはデータの中
ということが言えます。データは全て2つのダブルクォーテーションで囲われているわけですから、奇数の間はデータの中にいるわけです。
ですから、strTempがカンマだったときに
- quotCountが偶数:strTempをコロンに置き換える
- quotCountが奇数:strTempはそのままカンマ
としておきます。
Modですが
数値A Mod 数値B
は数値Aを数値Bで割った場合の余りを計算します。
LeftとRightを使って現在の文字列をコロンに置き換える
LeftとRightですが
Left(文字列, 文字数)
Rigth(文字列, 文字数)
文字列の最初から文字数分を切り出した、または文字列の最後から文字数分を切り出した文字列を返します。
str = Left(str, l - 1) & ":" & Right(str, Len(str) - l) '現在の1文字をコロンに置き換える
lは現在の位置ですから、Left(str, l – 1)は現在の位置より前の文字列です。
Len(str)はstrの長さ、lは現在の位置ですから、Right(str,Len(str) – l)は現在の位置より後の文字列です。
つまり、strの現在いる位置の文字をコロンに置き換わるだけです。
これで、データを分割すべきカンマはすべてコロンに置き換わり、データ内のカンマはそのまま維持されます。
実行結果
以上のプログラムを実行してみますと
ちゃんとズレずにCSVデータを取り込むことができました。
まとめ
これで、データの中にカンマが含まれている場合のCSVをエクセルVBAで取り込むことができました。
Mid,Left,Rightなどを使って文字列を切り出す処理や、ダブルクォーテーションが登場した数をカウントしてModで偶数個目か奇数個目かを判定する処理など、なかなか組みごたえのある内容だったかもしれません。
少しややこしい感じはありますが、数値にカンマが含まれているケースは結構出くわします。
ぜひマスターしちゃいましょう!
次回、CSVファイルの文字コードがUTF-8の場合の取り込み方を紹介できればと思います。
どうぞお楽しみに!
コメント
はじめまして。
最近調べもの中にこちらのサイトの存在を知り興味深く拝見させていただいております。
不躾ながら、この記事についての質問させてください。
21行目で arrLine = Split(Replace(replaceColon(strLine), “”””, “”), “:”)
と一旦 “,” を “:” に変換させる処理を行っていらっしゃいますが、
ここの処理は
arrLine = Split(strLine,”,”)
としておき、セルに値を入れる段階で
ws.Cells(i, j + 1).Value = Replace(arrLine(j),””””,””)
と処理するか、もしくは
ws.Cells(i, j + 1).Value = Replace(ws.Cells(i, j + 1).Value
と、セルの値に処理を行うと何か問題が発生するのでしょうか?
こちらの方が処理が簡単ですが、記事になさる場合に面白みがないため
わざと手の込んだ手法を取られたのかもしれませんが気になりましたので。
本来なら自分でCSVファイル等を作成して試行すべきなのですが、
こちらでの質問となりました事をご容赦ください。
すがもんさん
コメントありがとうございます。
いったんコロンにしている理由は記事内でもお伝えしていますが、「データ自体にカンマが含まれている場合(例えば”1,000円”など)は、そこで分割されてしまう(”1″と”000円”)という現象を防ぐためです。
コロンへのリプレイスは独自関数で「ダブルクォーテーションで囲まれてないところだけ」という条件にしているため、データ内のカンマはそのまま、データ外のカンマはコロンに変更後にsplitされます。
ご確認いただければと思います。
あ、なるほど!
私の認識不足で、前回の記事と今回の記事の内容がごっちゃになっておりました。
ダブルクォーテーションで囲まれている範囲内でもカンマで改行されてしまうのですね。
とても勉強になりました。
ありがとうございました。
良かったです!
引き続きよろしくお願いいたします!
はじめまして
このサイトで一から順に勉強させていただいている者です。
細かいことですが
21行目のコメントは
誤 strLineをカンマで区切りarrLineに格納
正 strLineをコロンで区切りarrLineに格納
ではないでしょうか?
ぴょんさん
コメントありがとうございます!
おっしゃる通りですね…ご指摘ありがとうございます。
修正をさせていただきました。
今後とも、弊ブログをよろしくお願いいたします!