簡単そうでエスケープ(,入力が必要な際のダブルクオーテーションによる例外化)処理やデリミタ変更(TSV)が以外と面倒なCSV処理.
標準ライブラリでやってみました.
書いてからEncoding指定もパラメータ化すれば良かったと思いましたが, そもそもクラス化してライブラリにした方がいいでしょうね.
読み込み
ファイル名指定
TextFieldParserを使うとファイル名も指定出来て楽.
けど, 後述するStream渡しの方が単機能になって個人的には好み.
Private Function LoadCSVFile(ByVal fileName As String) As List(Of List(Of String))
Dim ret As New List(Of List(Of String))
Dim parser As New FileIO.TextFieldParser(fileName,
System.Text.Encoding.GetEncoding("Shift_JIS"))
parser.TextFieldType = FileIO.FieldType.Delimited
parser.SetDelimiters(",")
parser.HasFieldsEnclosedInQuotes = True
parser.TrimWhiteSpace = True
While Not parser.EndOfData
Dim ret_row As New List(Of String)
Dim row As String() = parser.ReadFields()
For Each field As String In row
ret_row.Add(field)
Next
ret.Add(ret_row)
End While
Return ret
End Function
IO.Streamを指定
ファイル名ではなくIO.Streamを指定してやる.
こっちの方が外部IOが無い分, シンプルで良いと思う.
Private Function LoadCSVFromStream(ByVal csvStream As IO.Stream) As List(Of List(Of String))
Dim ret As New List(Of List(Of String))
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS")
Dim parser As New FileIO.TextFieldParser(csvStream, encoding)
parser.TextFieldType = FileIO.FieldType.Delimited
parser.SetDelimiters(",")
parser.HasFieldsEnclosedInQuotes = True
parser.TrimWhiteSpace = True
While Not parser.EndOfData
Dim ret_row As New List(Of String)
Dim row As String() = parser.ReadFields()
For Each field As String In row
ret_row.Add(field)
Next
ret.Add(ret_row)
End While
Return ret
End Function
StringをIO.Streamへ変換してやれば文字列渡しでも使えるし.
Private Function LoadCSVFromString(ByVal csvStr As String) As List(Of List(Of String))
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS")
Dim stream As New IO.MemoryStream(encoding.GetBytes(csvStr))
Return LoadCSVFromStream(stream)
End Function
書き込み
読み込みに比べれば随分楽です.
Private Function WriteCSV(ByVal stream As IO.StreamWriter, CSVList As List(Of List(Of String)), ByVal delimiter As String) As IO.StreamWriter
For i As Integer = 0 To CSVList.Count - 1
Dim row As List(Of String) = CSVList(i)
For k As Integer = 0 To row.Count - 1
Dim col As String = row(k)
If col.Contains(delimiter) = True Then
If col.Contains("""") = True Then
col = col.Replace("""", """""")
End If
col = """" & col & """"
End If
stream.Write(col)
If k <> row.Count - 1 Then
stream.Write(delimiter)
End If
Next
If i <> CSVList.Count - 1 Then
stream.Write(vbCrLf)
End If
Next
Return stream
End Function
使う時はこんな感じ.
Private Sub testCSVWrite()
Dim list As New List(Of List(Of String))
Dim r1 As New List(Of String)
r1.Add("abc")
r1.Add("def")
list.Add(r1)
Dim r2 As New List(Of String)
r2.Add("""abc""")
r2.Add("def")
list.Add(r2)
Dim r3 As New List(Of String)
r3.Add("test,string")
r3.Add("""zxd,")
list.Add(r3)
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS")
Dim path As String = System.IO.Path.Combine(
System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
"test.csv")
Dim sw As New IO.StreamWriter(path, False, encoding)
WriteCSV(sw, list, ",")
sw.Close()
End Sub