CSV file records

I am trying to read a CSV file into a datagrid (the csv file has 4 columns and up to 3000 records) and i found the following piece of code by JOE_MS which does exactly what i want:

Using parser As New Microsoft.VisualBasic.FileIO.TextFieldParser("c:\temp\myFile.csv")
parser.SetDelimiters(",")
While Not parser.EndOfData
Me.DataGridView1.Rows.Add(parser.ReadFields())
End
While
End
Using

However i cannot find the textfieldparser in my system. Do i need to add a reference or does this only work in VB.NET 2005 as i am using VB.NET 2003 If i can't get it in VB.NET 2003 how do i do it

Help please!!



Answer this question

CSV file records

  • Smacker

    -EDIT NOTE-

    For some reason the forum won't let me edit the post, but I want to point out that a minor error was found in the following code of the LoadDelimitedData routine:

    For Each flds As List(Of String) In ar

    If flds.Count > 0 Then

    If flds.Count > dt.Columns.Count Then

    For i As Integer = 0 To flds.Count - dt.Columns.Count

    dt.Columns.Add()

    Next

    End If

    Dim c As Integer = 0

    Dim dr As DataRow = dt.NewRow

    For Each s As String In flds

    dr.Item(c) = s

    c += 1

    Next

    dt.Rows.Add(dr)

    End If

    Next

    This code should actually read:

    For Each flds As List(Of String) In ar

    If flds.Count > 0 Then

    If flds.Count > dt.Columns.Count Then

    For i As Integer = 0 To flds.Count - dt.Columns.Count - 1

    dt.Columns.Add()

    Next

    End If

    Dim c As Integer = 0

    Dim dr As DataRow = dt.NewRow

    For Each s As String In flds

    dr.Item(c) = s

    c += 1

    Next

    dt.Rows.Add(dr)

    End If

    Next

    Thanks to Keith Blackwell for pointing this out!!



  • Tryin2Bgood

    Yes Microsoft.VisualBasic.FileIO.TextFieldParser is not available in .NET Framework 1.1.

    I guess you will have to use an System.IO.StreamReader, read each line as a single string and split it using the String.Split function.

    You have to be carefull with columns having a comma inside them! In this case columns are enclosed in double quotes.

    eg.
    value 1,value 2,"this is, value 3",value 4



  • spinal

    Hi
    I read through the article but still uncertain which one should I use. I could have drop your txtConnection and txtDataAdapter or use your long version code.

    Please let me know.

  • Niro

    Shame the textfieldparser way looks so simple. I have never done this streamreader way before and none of the examples i have seen show me enough so that i can do it. Can anyone show me how please It is as outlined at the start of the thread, the fields in each record can be any length.
  • Sandro Haupt

    Heres a function I wrote quite some time ago (took a while to find it!) that builds a DataTable on the fly based on a delimited text file. It handles the delimiting character exisiting inside of a quoted value and handles records with varied field counts:

    Public Function DelimitedTextToTable(ByVal FileName As String, ByVal Delimiter As Char, Optional ByVal FirstRowColumnNames As Boolean = False, Optional ByVal ColumnNames() As String = Nothing) As DataTable

    Dim dt As New DataTable(System.IO.Path.GetFileNameWithoutExtension(FileName))

    Dim SafeChar As Char = Chr(31)

    Dim fs As System.IO.StreamReader

    Try

    fs = System.IO.File.OpenText(FileName)

    Catch ex As Exception

    Throw New Exception("File not found, or error opening file. (" & FileName & ")", ex)

    End Try

    Dim str As String = fs.ReadToEnd

    fs.Close()

    Dim Recs() As String = str.Split(ControlChars.CrLf)

    If Recs.Length < 1 Then

    Throw New Exception("File opened successfuly, but no records were found.")

    End If

    Dim enm As IEnumerator = Recs.GetEnumerator

    If FirstRowColumnNames Then

    enm.MoveNext()

    Dim s As String = CType(enm.Current, String).Trim

    Dim fix() As Char = s.ToCharArray

    Dim InQuotes As Boolean = False

    For i As Integer = 0 To fix.Length - 1

    Select Case fix(i)

    Case Delimiter

    If Not InQuotes Then

    fix(i) = SafeChar

    End If

    Case ControlChars.Quote

    InQuotes = Not InQuotes

    End Select

    Next

    Dim sb As New System.Text.StringBuilder

    sb.Append(fix)

    Dim c() As String = sb.ToString.Split(SafeChar)

    Dim Cols(c.Length) As Data.DataColumn

    For i As Integer = 0 To c.Length - 1

    cols(i) = New DataColumn

    Cols(i).ColumnName = c(i)

    Cols(i).Caption = c(i)

    Next

    dt.Columns.AddRange(Cols)

    ElseIf Not IsNothing(ColumnNames) Then

    Dim cols(ColumnNames.Length) As Data.DataColumn

    For i As Integer = 0 To ColumnNames.Length - 1

    cols(i) = New DataColumn

    Cols(i).ColumnName = ColumnNames(i)

    Cols(i).Caption = ColumnNames(i)

    Next

    dt.Columns.AddRange(Cols)

    End If

    While enm.MoveNext

    Dim s As String = CType(enm.Current, String).Trim

    Dim fix() As Char = s.ToCharArray

    Dim InQuotes As Boolean = False

    For i As Integer = 0 To fix.Length - 1

    Select Case fix(i)

    Case Delimiter

    If Not InQuotes Then

    fix(i) = SafeChar

    End If

    Case ControlChars.Quote

    InQuotes = Not InQuotes

    End Select

    Next

    Dim sb As New System.Text.StringBuilder

    sb.Append(fix)

    Dim r() As String = sb.ToString.Split(SafeChar)

    If r.Length > dt.Columns.Count Then

    For i As Integer = dt.Columns.Count To r.Length - 1

    Dim col As New DataColumn

    col.ColumnName = "Auto_Column_" & i.ToString

    col.Caption = "Auto_Column_" & i.ToString

    dt.Columns.Add(col)

    Next

    End If

    For i As Integer = 0 To r.Length - 1

    Dim trims(0) As Char

    r(i) = r(i).Trim(New Char() {ControlChars.Quote})

    Next

    dt.Rows.Add(r)

    End While

    Return dt

    End Function

    Example usage might look like:

    Dim dt As New DataTable

    dt = Me.DelimitedTextToTable("c:\test2.csv", ",", True)

    DataGrid1.DataSource = dt

    HTH!



  • Tom2U


    *EDIT NOTE*
    This post was modified on 9/6/06 to correct a typo in the explanation and add two missing lines of code.


    There's still more to add, but here's a VS05 version of the TextDataProvider.  I didn't bother with the actual data provider implementations but this class can load a delimited text file into a DataSet or List(Of List(Of String)) and save changes back to a text file.

    This code can be dropped into a component file; just add a new component to the project and then paste this code over the pregenerated class code.  I'll post a link to the finished project and assembly when its complete (still want to add support for fixed width files, collection other than List(of List(of)), and DataColumn.DataType parsing).


    Public Class TextDataProvider

        Private myPath As String = String.Empty

        Private myDel As String = ","

        Private myFirstHeader As Boolean = False

        Private myLastLoad As New TimeSpan(0)

        Private myLastSave As New TimeSpan(0)
        Private myDataSet As New DataSet

     

        Public ReadOnly Property DataSet() As DataSet

            Get

                If Me.DesignMode Then

                    Return Nothing

                Else

                    Return Me.myDataSet

                End If

            End Get

        End Property

     

        Public Property Delimiter() As String

            Get

                Return Me.myDel

            End Get

            Set(ByVal value As String)

                If value = String.Empty Then

                    Me.myDel = ","

                Else

                    Me.myDel = value

                End If

            End Set

        End Property

     

        Public ReadOnly Property LastLoadTime() As TimeSpan

            Get

                Return Me.myLastLoad

            End Get

        End Property

     

        Public ReadOnly Property LastSaveTime() As TimeSpan

            Get

                Return Me.myLastSave

            End Get

        End Property

     

        Public Property FirstRowColumnHeaders() As Boolean

            Get

                Return Me.myFirstHeader

            End Get

            Set(ByVal value As Boolean)

                Me.myFirstHeader = value

            End Set

        End Property

     

        Public Property Path() As String

            Get

                Return Me.myPath

            End Get

            Set(ByVal value As String)

                Me.myPath = value

            End Set

        End Property

     

        Public Function GetData() As List(Of List(Of String))

            If myPath = String.Empty Then

                Throw New Exception("Path not set")

                Exit Function

            End If

     

            If Not System.IO.File.Exists(Me.myPath) Then

                Throw New System.IO.FileNotFoundException(myPath)

                Exit Function

            End If

     

            Dim fp As Microsoft.VisualBasic.FileIO.TextFieldParser = My.Computer.FileSystem.OpenTextFieldParser(Me.myPath)

            fp.TextFieldType = FileIO.FieldType.Delimited

            fp.TrimWhiteSpace = True

            fp.Delimiters = New String() {Me.myDel}

     

            Dim ar As New List(Of List(Of String))

            While Not fp.EndOfData

                ar.Add(New List(Of String)(fp.ReadFields))

            End While

            fp.Close()

            Return ar

        End Function

     

        Public Sub WriteData(ByVal data As List(Of List(Of String)))

            If myPath = String.Empty Then

                Throw New Exception("Path not set")

                Exit Sub

            End If

     

            Dim sb As New System.Text.StringBuilder

            For Each l As List(Of String) In data

                Dim isnew As Boolean = True

                For Each s As String In l

                    If Not isnew Then

                        sb.Append(Me.myDel)

                    Else

                        isnew = False

                    End If

                    sb.Append(s)

                Next

                sb.Append(ControlChars.NewLine)

            Next

            System.IO.File.WriteAllText(Me.myPath, sb.ToString)

        End Sub

     

        Public Function LoadDelimitedData() As DataSet

            If myPath = String.Empty Then

                Throw New Exception("Path not set")

                Exit Function

            End If

     

            Dim start As Date = Now

     

            If Not System.IO.File.Exists(Me.myPath) Then

                Throw New System.IO.FileNotFoundException(myPath)

                Exit Function

            End If

     

            Dim TableName As String = System.IO.Path.GetFileNameWithoutExtension(Me.myPath)

     

            Dim fp As Microsoft.VisualBasic.FileIO.TextFieldParser = My.Computer.FileSystem.OpenTextFieldParser(Me.myPath)

            fp.TextFieldType = FileIO.FieldType.Delimited

            fp.TrimWhiteSpace = True

            fp.Delimiters = New String() {Me.myDel}

     

            Dim ar As New List(Of List(Of String))

            While Not fp.EndOfData

                ar.Add(New List(Of String)(fp.ReadFields))

            End While

            fp.Close()

     

            If ar.Count > 0 Then

                Dim dt As DataTable

                If Not Me.myDataSet.Tables.Contains(TableName) Then

                    dt = New DataTable

                    dt.TableName = TableName

                    Me.myDataSet.Tables.Add(dt)

                Else

                    dt = Me.myDataSet.Tables(TableName)

                End If

     

                If myFirstHeader Then

                    For i As Integer = 0 To ar(0).Count - 1

                        If Not dt.Columns.Contains(ar(0)(i)) Then

                            dt.Columns.Add(ar(0)(i))

                        End If

                    Next

                    ar.RemoveAt(0)

                End If

     

                For Each flds As List(Of String) In ar

                    If flds.Count > 0 Then

                        If flds.Count > dt.Columns.Count Then

                            For i As Integer = 0 To flds.Count - dt.Columns.Count

                                dt.Columns.Add()

                            Next

                        End If

                        Dim c As Integer = 0

                        Dim dr As DataRow = dt.NewRow

     

                        For Each s As String In flds

                            dr.Item(c) = s

                            c += 1

                        Next

                        dt.Rows.Add(dr)

                    End If

                Next

            End If

     

            Me.myLastLoad = Now.Subtract(start)

            Return Me.myDataSet

        End Function

     

        Public Function LoadDelimitedData(ByVal ds As DataSet) As DataSet

            Me.myDataSet = ds

            Return Me.LoadDelimitedData

        End Function

     

        Public Function LoadDelimitedData(ByVal dt As DataTable) As DataSet

            Dim strsave As String = Me.myPath

            Me.myPath = System.IO.Path.GetDirectoryName(Me.myPath) & System.IO.Path.DirectorySeparatorChar & dt.TableName & System.IO.Path.GetExtension(Me.myPath)

            If Me.myDataSet.Tables.Contains(dt.TableName) Then

                dt = Me.DataSet.Tables(dt.TableName)

            Else

                Me.DataSet.Tables.Add(dt)

            End If

            Me.myPath = strsave

            Return Me.LoadDelimitedData

        End Function

     

        Public Sub SaveDelimitedData()

            If myPath = String.Empty Then

                Throw New Exception("Path not set")

                Exit Sub

            End If

     

            Dim strsav As String = Me.myPath

            For Each dt As DataTable In Me.myDataSet.Tables

                Me.myPath = System.IO.Path.GetDirectoryName(Me.myPath) & System.IO.Path.DirectorySeparatorChar & dt.TableName & System.IO.Path.GetExtension(Me.myPath)

                Me.SaveDelimitedData(dt)

            Next

            Me.myPath = strsav

        End Sub

     

        Public Sub SaveDelimitedData(ByVal dt As DataTable)

            If myPath = String.Empty Then

                Throw New Exception("Path not set")

                Exit Sub

            End If

     

            Dim start As Date = Now

            Dim sb As New System.Text.StringBuilder

     

            If dt.Rows.Count > 0 Then

                If Me.myFirstHeader Then

                    Dim isnew As Boolean = True

                    For Each col As DataColumn In dt.Columns

                        If Not isnew Then

                            sb.Append(Me.myDel)

                        Else

                            isnew = False

                        End If

                        sb.Append(col.ColumnName)

                    Next
                    sb.Append(ControlChars.NewLine)

                End If

                For Each dr As DataRow In dt.Rows

                    Dim isnew As Boolean = True

                    For Each col As DataColumn In dt.Columns

                        If Not isnew Then

                            sb.Append(Me.myDel)

                        Else

                            isnew = False

                        End If

                        sb.Append(dr.Item(col))

                    Next

                    sb.Append(ControlChars.NewLine)

                Next

                System.IO.File.WriteAllText(Me.myPath, sb.ToString)

     

            End If

            Me.myLastSave = Now.Subtract(start)

        End Sub

     

    End Class

     



  • Tinu

    It depends on your version of Visual Studio.

    If you are running Visual Studio 2003, use the txtConnection and txtDataAdapter from the download. If you are running Visual Studio 2005 copy and paste the TextDataProvider class. You can use a plain class file instead of a component if you want - just modify the DataSet property by removing the If statement that checks for DesignMode.



  • Senthil A

    impressive! Thanks for sharing!

  • Chris Baldwin - MSFT

    Here's a link to a TextDataProvider control.  This dll contains a DataConnection and DataAdapter for delimited text files (using the routine above).  You can use Add Items on the Toolbox to add these two components to VS.  Drop a txtConnection and a txtDataAdapter onto your form.  Set the connection string on the txtConnection to:  Provider=delimited;data source=c:\test2.csv;access mode=readwrite - where "source" is the path to your file.  Now you can use the Fill() method on the txtDataAdapter to fill a DataSet and the Update() method to write any changes to the DataTable back to a delimited text file.

    Here's the link to the compiled assembly.

    Here's a link to the project.

    I wrote these a while ago...  They work but are not completely error trapped.  You'll have to ensure that the connection string is formatted properly; provider should always be "delimited" and access mode can be "read", "write", or "readwrite".



  • CSV file records