读取CSV文件一些缺少的列

时间:2016-12-12 20:17:51

标签: .net vb.net csv import-csv

我正在尝试使用以下代码将CSV文件读入我的VB.net应用程序:

While Not EOF(1)
    Input(1, dummy)
    Input(1, phone_number)
    Input(1, username)
    Input(1, product_name)
    Input(1, wholesale_cost)
    Input(1, dummy)
    Input(1, dummy)
End While

我的CSV文件(如文字)如下所示:

Customer Name,Phone Number,Username,Product,Wholesale Cost,Sales Price,Gross Profit, Customer Reference
  ,00000000000,00000000000,Product Name,25.00,35.00,10.00,
  ,00000000000,00000000000,Product Name,1.00,1.40,0.40,

如您所见,并非所有字段都包含在内,因此在读取文件时会显示错误,因为它无法到达行尾。

我该如何处理这类文件?

有时字段会在某些行上显示,而其他字段则不存在。

更新

我已经尝试了 Zenacity 提供的答案,但在尝试使用循环内的sArray(1)进行阅读时,它会返回Index was outside the bounds of the array

3 个答案:

答案 0 :(得分:11)

您应该掌握的一点是,Filexxxx方法只是正式和正式弃用。使用它们时,Intellisense弹出:

  

...我的功能为您提供了比FileOpen更高的文件I / O操作的生产力和性能。有关更多信息,请参阅Microsoft.VisualBasic.FileIO.FileSystem。

他们在讨论My.Computer.FileSystem但是有一些更有用的.NET方法。

帖子没有透露数据将如何存储,但如果它是任何种类和/或结构的数组,那么如果不过时则至少是次优的。这会将它存储在一个类中,以便数字数据可以存储为数字,并使用List代替数组。

我使用一些随机数据制作了一个类似于你的快速文件:{"CustName", "Phone", "UserName", "Product", "Cost", "Price", "Profit", "SaleDate", "RefCode"}

  • CustName在70%的时间内存在
  • 用户名永远不会出现
  • RefCode有30%的时间存在
  • 我添加了 SaleDate 来说明数据转换
  

Ziggy Aurantium,132-5562 ,, Cat Food,8.26,9.95,1.69,08 / 04/2016,
  Catrina Caison,899-8599,Knife Sharpener,4.95,6.68,1.73,10 / 12/2016,X-873-W3
  ,784-4182 ,,蒸汽压缩机,11.02,12.53,1.51,09 / 12/2016,

解析CSV的代码

注意:这是一种解析CSV的错误方法。这样做有很多问题可以解决;加上需要更多代码。它的呈现是因为它是一种不必处理缺失字段的简单方法。请参阅正确方法

' form/class level var:
Private SalesItems As List(Of SaleItem)

SaleItem是一个存储您关注的元素的简单类。 SalesItems是一个可以存储 SaleItem个对象的集合。该类中的属性允许将价格费用存储为Decimal,将日期存储为DateTime

' temp var
Dim item As SaleItem
' create the collection
SalesItems = New List(Of SaleItem)

' load the data....all of it
Dim data = File.ReadAllLines("C:\Temp\custdata.csv")

' parse data lines 
' Start at 1 to skip a Header
For n As Int32 = 0 To data.Length - 1
    Dim split = data(n).Split(","c)

    ' check if it is a good line
    If split.Length = 9 Then
        ' create a new item
        item = New SaleItem
        ' store SOME data to it
        item.CustName = split(0)
        item.Phone = split(1)
        ' dont care anout user name (2)
        item.Product = split(3)
        ' convert numbers
        item.Price = Convert.ToDecimal(split(4))
        item.Cost = Convert.ToDecimal(split(5))
        ' dont use the PROFIT, calculate it in the class (6)

        ' convert date
        item.SaleDate = Convert.ToDateTime(split(7))

        ' ignore nonexistant RefCode (8)

        ' add new item to collection
        ' a List sizes itself as needed!
        SalesItems.Add(item)
    Else
        ' To Do: make note of a bad line format
    End If
Next

' show in DGV for approval/debugging
dgvMem.DataSource = SalesItems

结果: enter image description here

备注
存储可以简单计算的东西通常是个坏主意。所以Profit属性是:

Public ReadOnly Property Profit As Decimal
    Get
        Return (Cost - Price)
    End Get
End Property

如果更新成本或价格,它永远不会“陈旧”。

如图所示,使用生成的集合可以非常容易地显示给用户。给定DataSourceDataGridView将创建列并填充行。

正确的方式

String.Split(c) 是一个非常糟糕的主意,因为如果产品是:"Hose, Small Green",它会将其删除并将其视为2个字段。有许多工具可以为您完成几乎所有的工作:

  1. 阅读文件
  2. 解析线
  3. 将CSV数据映射到班级
  4. 将文本转换为正确的数据类型
  5. 创建一个经济的收藏家
  6. 除了课程外,上述所有内容都可以使用 CSVHelper 在几行内完成:

    Private CustData As List(Of SaleItem)
    ...
    Using sr As New StreamReader("C:\Temp\custdata.csv", False),
         csv = New CsvReader(sr)
        csv.Configuration.HasHeaderRecord = True
    
        CustData = csv.GetRecords(Of SaleItem)().ToList()
    End Using
    

    两行或三行代码,用于读取,解析和创建250个项目的集合。

    出于某种原因,即使你想手动,CSVHelper也可以提供帮助。您可以使用它来读取和解析数据,而不是为您创建List(Of SaleItem)

    ... like above
    csv.Configuration.HasHeaderRecord = True
    Do Until csv.Read() = False
        For n As Int32 = 0 To csv.Parser.FieldCount - 1
            DoSomethingWith(csv.GetField(n))
        Next
    Loop
    

    这会逐一将字段返回给您。它不会转换任何日期或价格,但它也不会扼杀丢失的数据元素。

    资源

答案 1 :(得分:5)

警告:如果CustomerNameProductName值可以包含逗号' s (。即CustomerName = "Callaway , Mark"您无法使用String.Split()方法。并且最好搜索第三方csv解析器,或者你可以使用TextFieldParser Class - > MSDN article

我的回答是假设缺少的字段始终位于该行的右侧,且字段值不包含逗号 (否则@Plutonix答案为你在寻找什么)

使用此代码,您可以导入缺少字段的行。

您必须从csv文件中读取每一行,使用以下代码计算此行中的","出现次数

Line.Count(Function(c As Char) c = ",")

如果小于7(8列),您将添加缺失的","

 String.PadRight((7 - intCommaCount), ",")

注意: 如果缺少逗号来自左侧,则可以使用String.PadLeft((7 - intCommaCount), ",")

将该行拆分为Item属性

我创建了以下Item

Public Class MyItem


Public Property CustomerName As String
Public Property PhoneNumber As String
Public Property Username As String
Public Property Product As String
Public Property WholesaleCost As String
Public Property SalesPrice As String
Public Property GrossProfit As String
Public Property CustomerReference As String

Public Shared Function CreateObjectFromLine(ByVal Line As String) As MyItem

    'Count Comma occurence in Line
    Dim intCommaCount As Integer = Line.Count(Function(c As Char) c = CChar(","))
    Dim strTemp = Line

    'Add missing comma's
    If intCommaCount < 7 Then

        strTemp = strTemp.PadRight((7 - intCommaCount), ",")

    End If

    'Split Line and return MyItem Class
    Dim str() As String = strTemp.Split(",")

    Return New MyItem With {.CustomerName = str(0),
        .PhoneNumber = str(1),
        .Username = str(2),
        .Product = str(3),
        .WholesaleCost = str(4),
        .SalesPrice = str(5),
        .GrossProfit = str(6),
        .CustomerReference = str(7)}




End Function



End Class

我使用以下代码从CSV文件导入数据

    Dim SalesItems As New List(Of MyItem)
    Dim csvFile As String = "C:\1.csv"


    Using csvStreamReader As New IO.StreamReader(csvFile)

        While Not csvStreamReader.EndOfStream

       Dim strLine as string = csvStreamReader.ReadLine

       ' Skip Header
       If strLine.StartsWith("Customer Name") Then Continue While

            Dim item As MyItem = MyItem.CreateObjectFromLine(strLine)

            SalesItems.Add(item)



        End While


    End Using

    'Showing Result in a DataGridView
    dgvItems.DataSource = SalesItems

注意: 这是一个简单的示例,需要添加错误处理Try... CatchNull正在检查

答案 2 :(得分:4)

通过使用以下函数,您可以逐行评估文件内容并执行相应的操作。

Imports System.IO    
Private Sub ParseCSVFile(psFile As String)
    Dim sArray() As String
    Dim Customer_Name As String = String.Empty
    Dim Phone_Number As String = String.Empty
    Dim Username As String = String.Empty
    Dim Product As String = String.Empty
    Dim Wholesale_Cost As String = String.Empty
    Dim Sales_Price As String = String.Empty
    Dim Gross_Profit As String = String.Empty
    Dim Customer_Reference As String = String.Empty

    Try
        Using objStreamReader As StreamReader = New StreamReader(psFile) 'should be full path
            Dim sLine As String = String.Empty
            Do
                sLine = objStreamReader.ReadLine()
                If sLine <> Nothing Then
                    sArray = Split(sLine, ",")
                    Customer_Name = sArray(0)
                    Phone_Number = sArray(1)
                    Username = sArray(2)
                    Product = sArray(3)
                    Wholesale_Cost = sArray(4)
                    Sales_Price = sArray(5)
                    Gross_Profit = sArray(6)
                    Customer_Reference = sArray(7)
                    Debug.Print(Customer_Name & "," & Phone_Number & "," & Username & "," & Product & "," & Wholesale_Cost & "," & Sales_Price & "," & Gross_Profit & "," & Customer_Reference)
                End If
            Loop Until sLine Is Nothing
        End Using
    Catch
        'log error
    End Try
End Sub