none
Finding the index of a line of data after performing a 'contains' search in a csv file RRS feed

  • Question

  • I am trying to get the index from a csv file that has my 'contains' data.

    My current code:

    Using reader As New StreamReader(StartForm.GlobalVariables.moldFile)
                While Not reader.EndOfStream
                    Dim line As String = reader.ReadLine()
                    If line.Contains(StartForm.GlobalVariables.deleteMoldName) Then
    
                        Dim foundString As String = line
    
                        ' get index of found mold word match from molds.csv file
                        Dim foundIndex = foundString.IndexOf(line)
                        MsgBox(foundIndex)
    
                        ' remove line of mold data
    
    
                        Exit While
                    End If
                End While
            End Using
    All I am getting is index=0 from a msgbox pop-up I added for checking the value.
    Thanks in advance.


    • Edited by Pete - Wednesday, June 13, 2018 5:49 PM
    Wednesday, June 13, 2018 5:48 PM

Answers

  • As an aside, you can eliminate the Delete call by using IO.File.Open("path to file", IO.FileMode.OpenOrCreate) which will create a new file if one does not exist, or open the existing file to be overwritten if it does exist.  In the case of an operation which cannot complete without the existing file you can just use FileMode.Open after ensuring that the file exists.

    Also, ArrayList is a throwback class that is rarely used in new development.  Generally a generic List(Of T) is preferable and in this case would be a List(Of String).

    One more tip, which is design related...

    When writing code for object oriented programming (OOP) it is important to follow standard practices of code design in order to avoid confusion and bugs, particularly as the code grows in scope and complexity.

    The first potential problem is with StartForm.GlobalVariables.  I'm not sure if this code is located in StartForm and GlobalVariables is a shared field/property or if you are accessing the default instance of StartForm, but either way, the method should not be tied to this GlobalVariables object.  And if you are using the default form instance, that should be avoided as well (Default form instances were for the VB6 upgrade wizard which no longer exists - they are not meant to be used in new projects).

    In object oriented design, any parameters needed by a method should be pass to that method in its arguments.  The idea is that you want "loosely coupled" methods and what you have now are "tightly coupled methods"... that is, the method which works with the data is dependent on the user interface code, the two are tightly coupled together.

    In this case your delete routine should be in its own method with appropriate arguments.  We can also implement the other changes mentioned above and refactor the streams to use a single file stream since your reading and writing operations are sequential.  For example:

    Private Sub DeleteMold(moldName As String, fileName As String)
        If Not IO.File.Exists(fileName) Then Exit Sub
        Dim line As String
        'only need one file stream for both reading and writing since the two operations are sequential
        Dim baseStream = IO.File.Open(fileName, IO.FileMode.Open)
        Dim strFile As New List(Of String)
        'create stream reader and stream writer from single base stream
        Using Input = New IO.StreamReader(baseStream), objWriter = New IO.StreamWriter(baseStream)
            ' Loop through the file
            While Input.Peek <> -1
                ' Read the next available line
                line = Input.ReadLine
    
                ' Check to see if the line contains what you're looking for.
                ' If not, add it to a List
                If Not line.Contains(moldName) Then
                    ' If not, add it to a List
                    strFile.Add(line)
                End If
            End While
    
            'reset the filestream
            baseStream.Position = 0
            baseStream.SetLength(0)
    
            ' Iterate through the List
            For Each item As String In strFile
                ' Write the current item in the List to the file.
                objWriter.WriteLine(item)
            Next
            objWriter.Flush()
        End Using
    End Sub
    

    Then you can call this function from your button click handler, passing the appropriate arguments:

    Private Sub btnDeleteMold_Click(sender As Object, e As EventArgs) Handles btnDeleteMold.Click
    
        DeleteMold(lstMoldStock.SelectedItem.ToString, moldFilePath)
    
        txtName.Text = ""
        txtDimensions.Text = ""
        txtGallons.Text = ""
        txtWeight.Text = ""
    
        ' reload mold stock file into listbox
        lstMoldStock.Items.Clear()
        Functions.LoadMoldStock()
    End Sub
    

    Notice that now there is nothing in the DeleteMold method that ties it to any particular form, controls, or shared object.  The method is decoupled from the user interface.  This is proper OOP design.

    Now, you are left with getting the variable "moldFilePath" a value, but that should be acquired from wherever it is stored.  If this is an application scoped variable (eg one value per user instance of the application) then placing it in My.Settings (using My Project -> Settings from the Solution Explorer) would be a good choice.  Then your button click event handler could simply use My.Settings.MoldFilePath to get the value.  Again this decouples the data value from the user interface.

    It also would appear that Functions.LoadMoldStock() is not designed properly for OOP.  Presumably LoadMoldStock is a method for displaying information in the user interface so it should belong to whatever Form it is displaying information in.  It may also be something that should be refactored into two methods - one which loads the data into an object model and one which displays that object model in a user interface. I don't have a good way to show this because you currently don't use an object model for your data and I can only guess at what this function actually does.  But following the general principle of decoupling as shown above, along with encapsulation (ensuring that only relevant, related code is grouped together in an appropriate object) should lead you to a better OOP design for this part of the code.

    If you are indeed rereading the mold file in LoadMoldStock then you really should consider using the object model.  Reading the file should occur when the user starts using the program and writing out the final state of the data back to a file should occur when the user is done using the program.  All operations in between should be performed against the in-memory object model representing the data.

    I know that this may all be a lot to absorb but it is important information to consider as your projects become larger and more complex.  Hopefully this makes enough sense to help you begin to steer in the direction of OOP design.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by Pete - Thursday, June 14, 2018 1:26 PM
    Thursday, June 14, 2018 11:15 AM
    Moderator

All replies

  • Hi

    It is not entirely clear what you are wanting to do. I have made a guess, probably wrong, but here is my take on it.

    This example will delete all lines in a text file which contain the given text string (ignoring case).

    It loads all text lines, deletes appropriate lines and writes remaining lines to same file, overwriting existing text.

    Option Strict On Option Explicit On Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' example use

    RemoveLine("aaaaa", "C:\Users\lesha\Desktop\NpowerQuote.txt") End Sub Sub RemoveLine(stringtoremove As String, filepath As String) Dim doc As List(Of String) = IO.File.ReadAllLines(filepath).ToList For line As Integer = doc.Count - 1 To 0 Step -1 If doc(line).ToLower.Contains(stringtoremove.ToLower) Then doc.RemoveAt(line) End If Next IO.File.WriteAllLines(filepath, doc) End Sub End Class



    Regards Les, Livingston, Scotland


    • Edited by leshay Wednesday, June 13, 2018 6:07 PM
    Wednesday, June 13, 2018 6:07 PM
  • Your variables "foundString" and "line" both point to the same string instance so you will always get index 0 when looking for the start index of a string within that same string.

    Since the code is reading and analyzing one line at a time, the found line index will be the count of lines already analyzed:

    Dim lineCount As Integer = 0
    Using reader As New StreamReader(StartForm.GlobalVariables.moldFile)
        While Not reader.EndOfStream
            Dim line As String = reader.ReadLine()
            If line.Contains(StartForm.GlobalVariables.deleteMoldName) Then
                ' get index of found mold word match from molds.csv file
                Dim foundIndex = lineCount
    
                ' remove line of mold data
                Exit While
            End If
            lineCount += 1
        End While
    End Using
    

    But since you then appear to want to remove the line, you will need to read the entire file and rewrite it without the desired line.

    That being the case, you should use a different methodology such as reading the lines into a list (or writing them directly to another file), skipping the line which matches your search criteria.  The index of the line is probably not relevant to the desired outcome.

    If we knew more about the overall goal (not just the goal of this one piece of code), then we might offer better suggestions.  My initial suspicion is that you want to load the CSV file into a matching object model, allow the user to manipulate that model (add/remove/edit items) and then write the updated model back out to a CSV file.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, June 13, 2018 6:14 PM
    Moderator
  • I know how that is when you don't know the goal.

    I have a CSV file that contains delimited data for a mold:

    ex. "Bench Leg,32",6,101"

    and there are several more. So, within the program a user selects a mold to delete from a list box.

    Once a user clicks on it, it will get to the sub I have posted.

    The bottom line is to:

    Find the 1st comma delimited data- ex. "Bench" from the list box the user has clicked on, and find the matching data line from the CSV file and remove it.

    I have not worked on removal yet- just was going one step at a time. I was trying to get the index number from the CSV file for that line of data to be removed.

    I have to do it this way because my list box is 'sorted'- so it will not match directly to my CSV file.

    Hope that helps, Thanks


    • Edited by Pete - Wednesday, June 13, 2018 6:30 PM
    Wednesday, June 13, 2018 6:27 PM
  • Here is my complete sub code:

        Private Sub btnDeleteMold_Click(sender As Object, e As EventArgs) Handles btnDeleteMold.Click
    
            ' empty search string variable
            StartForm.GlobalVariables.deleteMoldName = ""
    
            ' get selected items value and assign into global variable
            StartForm.GlobalVariables.deleteMoldName = lstMoldStock.SelectedItem.ToString
    
            ' code to delete selected mold
            Using reader As New StreamReader(StartForm.GlobalVariables.moldFile)
                While Not reader.EndOfStream
                    Dim line As String = reader.ReadLine()
                    If line.Contains(StartForm.GlobalVariables.deleteMoldName) Then
    
                        Dim foundString As String = line
    
                        ' get index of found mold word match from molds.csv file
                        Dim foundIndex = foundString.IndexOf(line)
                        MsgBox(foundIndex)
    
                        ' remove line of mold data
    
    
                        Exit While
                    End If
                End While
            End Using
    
            ' reload mold stock file into listbox
            lstMoldStock.Items.Clear()
            Functions.LoadMoldStock()
    
        End Sub



    • Edited by Pete - Wednesday, June 13, 2018 6:33 PM
    Wednesday, June 13, 2018 6:31 PM
  • Ignore this if not interested in removing a line but instead only indexing it.

    If you intent is to remove line(s) that contain a string then you need to do something like this.

    Public Class FileOperations
        Private mHasException As Boolean
        Public ReadOnly Property HasException As Boolean
            Get
                Return mHasException
            End Get
        End Property
        Private mException As Exception
        Public ReadOnly Property LastException As Exception
            Get
                Return mException
            End Get
        End Property
        ''' <summary>
        ''' This method is overkill for small files, 
        ''' the intent is to work on very large files.
        ''' </summary>
        ''' <param name="pOriginalFile">File to read</param>
        ''' <param name="pOutputFile">Write results too</param>
        ''' <param name="pSearchTerm">Search for</param>
        ''' <returns></returns>
        ''' <remarks>Should be asynchronous either within the method or 
        ''' caller wraps this method in a Task.Run via await</remarks>
        Public Function ReplaceTextInFile(
            ByVal pOriginalFile As String,
            ByVal pOutputFile As String,
            ByVal pSearchTerm As String) As Boolean
    
            mHasException = False
    
            Try
                Dim tempLineValue As String
                Using inputStream As FileStream = File.OpenRead(pOriginalFile)
                    Using inputReader As New StreamReader(inputStream)
                        Using outputWriter As StreamWriter = File.AppendText(pOutputFile)
                            tempLineValue = inputReader.ReadLine()
                            Do While Nothing IsNot tempLineValue
    
                                If Not tempLineValue.Contains(pSearchTerm) Then
                                    outputWriter.WriteLine(tempLineValue)
                                End If
                                tempLineValue = inputReader.ReadLine()
                            Loop
                        End Using
                    End Using
                End Using
    
                File.Delete(pOriginalFile)
                File.Move(pOutputFile, pOriginalFile)
    
                mHasException = False
            Catch ex As Exception
    
                mHasException = True
                mException = ex
    
            End Try
    
            Return Not mHasException
    
        End Function
    
    End Class


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Wednesday, June 13, 2018 6:33 PM
    Moderator
  • I know how that is when you don't know the goal.

    I have a CSV file that contains delimited data for a mold:

    ex. "Bench Leg,32",6,101"

    and there are several more. So, within the program a user selects a mold to delete from a list box.

    Once a user clicks on it, it will get to the sub I have posted.

    The bottom line is to:

    Find the 1st comma delimited data- ex. "Bench" from the list box the user has clicked on, and find the matching data line from the CSV file and remove it.

    I have not worked on removal yet- just was going one step at a time. I was trying to get the index number from the CSV file for that line of data to be removed.

    I have to do it this way because my list box is 'sorted'- so it will not match directly to my CSV file.

    Hope that helps, Thanks


    Hi

    I think that is what the code I posted does.


    Regards Les, Livingston, Scotland

    Wednesday, June 13, 2018 6:38 PM
  • Keep in mind that every change requires that you recreate the entire text file in order to save that change... its one of the reasons for using a database instead of a text file for storing data.

    How many changes do you anticipate the user performing on the data?  If this program's only purpose is to delete molds from a file, then you can probably continue with something similar to what you have.  But if the purpose of the program is to manage the file contents, including adding new molds or editing existing molds or doing some other work with the existing molds, then you probably don't want to work directly against the CSV file for each individual operation.

    Instead you would want to load the CSV file at the start of the program (or when the user choose to "open" the file) and read the whole thing, creating an in-memory version of the file's contents.  All of the user operations then work against this in-memory version of the data until the user closes the program or chooses to "save" the file.  At that point you write the data back out overtop of the original file, thus persisting the changes.

    And then, rather than trying to deal with a bunch of unidentified lines of text, you would be much better off creating code objects that model each data element.  So for molds, you would write a class called "Mold" which would have properties for each "field" in the CSV file, plus any other useful information (eg. Name, length, width, depth, material, etc.).  When reading the CSV file you would create an instance of the Mold object and populate its properties with the data from the file.  The rest of your program then needs only work with the Mold object instances to make changes.  Finally you can loop over the collection of Mold objects and write their property values back out to a CSV file when you are ready to save your changes.

    So think of the CSV file as only the persistence of the data and use an object model which maps to that data as the editable objects with which users of your program will interact.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, June 13, 2018 6:40 PM
    Moderator
  • Hi Karen and thanks for helping.

    It is only a small file with maybe 200 entries max.

    So, I wasn't getting crazy on the code.

    You must type fast or you have snippets available...lol

    I can try your code, it takes some time to analyze what you are doing and follow it, so it will take some time.

    Thanks, and if you can think of something simpler, let me know.

    Wednesday, June 13, 2018 6:41 PM
  • Ok, Les, Thank you.

    I will have to try it later, as I am going out on the river fishing now.

    I will be back tonight.

    Thanks again.

    Wednesday, June 13, 2018 6:59 PM
  • In regards to 200 lines, you can find using lambda. The following is a demo that first creates a anonymous type in the select then the where uses it to find a string and return the line number.

    Products.txt is in the same folder as the executable.

    Imports System.IO
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim findThis As String = "24 - 200 g pkgs.     32.00"
    
            Dim results = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Products.txt")).
                Select(Function(line, index) New With {.Text = line, .Row = index}).
                Where(Function(item) item.Text.Contains(findThis)).
                FirstOrDefault
    
            If results IsNot Nothing Then
                Console.WriteLine(results.Row)
            End If
        End Sub
    End Class
    
    PS: Would had been faster but I was at lunch.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Wednesday, June 13, 2018 7:04 PM
    Moderator
  • Haha, I was using lamda in MVC, didn't know you could use it in VB.
    Thursday, June 14, 2018 12:31 AM
  • Ok, thanks for everyones help.

    I did solve it by using the following sub code:

    Private Sub btnDeleteMold_Click(sender As Object, e As EventArgs) Handles btnDeleteMold.Click
    
            ' empty search string variable
            StartForm.GlobalVariables.deleteMoldName = ""
    
            ' get selected items value and assign into global variable
            StartForm.GlobalVariables.deleteMoldName = lstMoldStock.SelectedItem.ToString
    
    
            Dim line As String
            Dim Input As StreamReader
            Dim strFile As New ArrayList
            Input = File.OpenText(StartForm.GlobalVariables.moldFile)
    
            ' Loop through the file
            While Input.Peek <> -1
                ' Read the next available line
                line = Input.ReadLine
    
                ' Check to see if the line contains what you're looking for.
                ' If not, add it to an ArrayList
                If Not line.Contains(StartForm.GlobalVariables.deleteMoldName) Then
                    ' If not, add it to an ArrayList
                    strFile.Add(line)
                End If
            End While
    
            Input.Close()
    
            ' Because we want to replace the content of the file, we first
            ' need to delete it.
            If File.Exists(StartForm.GlobalVariables.moldFile) Then
                File.Delete(StartForm.GlobalVariables.moldFile)
            End If
    
            ' Create a StreamWriter object with the same filename
            Dim objWriter As New System.IO.StreamWriter(StartForm.GlobalVariables.moldFile, True)
            ' Iterate through the ArrayList
            For Each item As String In strFile
                ' Write the current item in the ArrayList to the file.
                objWriter.WriteLine(item)
            Next
            objWriter.Flush()
            objWriter.Close()
    
            txtName.Text = ""
            txtDimensions.Text = ""
            txtGallons.Text = ""
            txtWeight.Text = ""
    
            ' reload mold stock file into listbox
            lstMoldStock.Items.Clear()
            Functions.LoadMoldStock()
    
        End Sub

    Thursday, June 14, 2018 3:44 AM
  • Hi Pete,

    I am glad to know that you resolve the issue and share for sharing the solution to us, please mark it as answer, it will be beneficial to other communities who have the similar

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, June 14, 2018 8:04 AM
    Moderator
  • Hi Pete,

    As Zhanglong noted, we're glad you found a solution and ask that you close your thread by clicking the Mark as answer link at the bottom of one or more relevant posts; however, you rarely mark your own post as answer unless you came up with a solution independent of any contributions toward your question.  The most correct way to close the thread is by selecting the replies which led to your final solution.

    Thanks!


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Thursday, June 14, 2018 10:14 AM
    Moderator
  • As an aside, you can eliminate the Delete call by using IO.File.Open("path to file", IO.FileMode.OpenOrCreate) which will create a new file if one does not exist, or open the existing file to be overwritten if it does exist.  In the case of an operation which cannot complete without the existing file you can just use FileMode.Open after ensuring that the file exists.

    Also, ArrayList is a throwback class that is rarely used in new development.  Generally a generic List(Of T) is preferable and in this case would be a List(Of String).

    One more tip, which is design related...

    When writing code for object oriented programming (OOP) it is important to follow standard practices of code design in order to avoid confusion and bugs, particularly as the code grows in scope and complexity.

    The first potential problem is with StartForm.GlobalVariables.  I'm not sure if this code is located in StartForm and GlobalVariables is a shared field/property or if you are accessing the default instance of StartForm, but either way, the method should not be tied to this GlobalVariables object.  And if you are using the default form instance, that should be avoided as well (Default form instances were for the VB6 upgrade wizard which no longer exists - they are not meant to be used in new projects).

    In object oriented design, any parameters needed by a method should be pass to that method in its arguments.  The idea is that you want "loosely coupled" methods and what you have now are "tightly coupled methods"... that is, the method which works with the data is dependent on the user interface code, the two are tightly coupled together.

    In this case your delete routine should be in its own method with appropriate arguments.  We can also implement the other changes mentioned above and refactor the streams to use a single file stream since your reading and writing operations are sequential.  For example:

    Private Sub DeleteMold(moldName As String, fileName As String)
        If Not IO.File.Exists(fileName) Then Exit Sub
        Dim line As String
        'only need one file stream for both reading and writing since the two operations are sequential
        Dim baseStream = IO.File.Open(fileName, IO.FileMode.Open)
        Dim strFile As New List(Of String)
        'create stream reader and stream writer from single base stream
        Using Input = New IO.StreamReader(baseStream), objWriter = New IO.StreamWriter(baseStream)
            ' Loop through the file
            While Input.Peek <> -1
                ' Read the next available line
                line = Input.ReadLine
    
                ' Check to see if the line contains what you're looking for.
                ' If not, add it to a List
                If Not line.Contains(moldName) Then
                    ' If not, add it to a List
                    strFile.Add(line)
                End If
            End While
    
            'reset the filestream
            baseStream.Position = 0
            baseStream.SetLength(0)
    
            ' Iterate through the List
            For Each item As String In strFile
                ' Write the current item in the List to the file.
                objWriter.WriteLine(item)
            Next
            objWriter.Flush()
        End Using
    End Sub
    

    Then you can call this function from your button click handler, passing the appropriate arguments:

    Private Sub btnDeleteMold_Click(sender As Object, e As EventArgs) Handles btnDeleteMold.Click
    
        DeleteMold(lstMoldStock.SelectedItem.ToString, moldFilePath)
    
        txtName.Text = ""
        txtDimensions.Text = ""
        txtGallons.Text = ""
        txtWeight.Text = ""
    
        ' reload mold stock file into listbox
        lstMoldStock.Items.Clear()
        Functions.LoadMoldStock()
    End Sub
    

    Notice that now there is nothing in the DeleteMold method that ties it to any particular form, controls, or shared object.  The method is decoupled from the user interface.  This is proper OOP design.

    Now, you are left with getting the variable "moldFilePath" a value, but that should be acquired from wherever it is stored.  If this is an application scoped variable (eg one value per user instance of the application) then placing it in My.Settings (using My Project -> Settings from the Solution Explorer) would be a good choice.  Then your button click event handler could simply use My.Settings.MoldFilePath to get the value.  Again this decouples the data value from the user interface.

    It also would appear that Functions.LoadMoldStock() is not designed properly for OOP.  Presumably LoadMoldStock is a method for displaying information in the user interface so it should belong to whatever Form it is displaying information in.  It may also be something that should be refactored into two methods - one which loads the data into an object model and one which displays that object model in a user interface. I don't have a good way to show this because you currently don't use an object model for your data and I can only guess at what this function actually does.  But following the general principle of decoupling as shown above, along with encapsulation (ensuring that only relevant, related code is grouped together in an appropriate object) should lead you to a better OOP design for this part of the code.

    If you are indeed rereading the mold file in LoadMoldStock then you really should consider using the object model.  Reading the file should occur when the user starts using the program and writing out the final state of the data back to a file should occur when the user is done using the program.  All operations in between should be performed against the in-memory object model representing the data.

    I know that this may all be a lot to absorb but it is important information to consider as your projects become larger and more complex.  Hopefully this makes enough sense to help you begin to steer in the direction of OOP design.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by Pete - Thursday, June 14, 2018 1:26 PM
    Thursday, June 14, 2018 11:15 AM
    Moderator
  • Thanks for your contribution, Reed.

    The 'GlobalVariable' is something I have used for years to be able to talk across many classes, when there are 50+ classes, and >45,000 lines of code.

    I have developed major production software programs in the last 15 years.

    This quick program was too simple and I was not used to it...... I was just throwing it together quick.

    You know what I mean, Complex-easy, Simple-hard.

    I have read through your post, and understand what you are saying.

    I felt 'not-so-bright' by even asking the question, as I carry an AAS in Computer Programming and Web Development and have been programming since 1973.

    Things have changed so much since those days- I remember starting with BASIC and have seen so many, and experienced some like FORTRAN (Formula Translation), that is unbelievable.

    I appreciate all the quick replies in this forum when I need assistance.

    And I thank all that contributed.

    -Pete

    Thursday, June 14, 2018 1:18 PM
  • Is the CSV something you created? First question that comes to mind is why you're using a CSV file.

    Live as if you were going to die today, learn as if you were going to live forever -Mahatma Gandhi

    Thursday, June 14, 2018 6:14 PM
  • Yes, I manually created it for a starter file.

    Simplicity. It could have been a text file or an xml file- didn't matter. And too small for a DB.

    • Edited by Pete - Thursday, June 14, 2018 9:08 PM
    Thursday, June 14, 2018 9:06 PM