读取CSV文件一些缺少的列

cha*_*lie 12 .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
Run Code Online (Sandbox Code Playgroud)

我的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,
Run Code Online (Sandbox Code Playgroud)

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

我该如何处理这种类型的文件?

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

UPDATE

我已经尝试过Zenacity提供的答案,但是当尝试sArray(1)在循环内部使用时它会返回Index was outside the bounds of the array

Ňɏs*_*arp 11

您应该掌握的一件事是,这些Filexxxx方法几乎都是正式和正式弃用的.使用它们时,Intellisense弹出:

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

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

该帖子没有揭示数据将如何存储,但如果它是任何种类和/或结构的数组,那么如果不过时则至少是次优的.这会将它存储在一个类中,以便数字数据可以存储为数字,而a 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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述

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

Public ReadOnly Property Profit As Decimal
    Get
        Return (Cost - Price)
    End Get
End Property
Run Code Online (Sandbox Code Playgroud)

如果更新成本或价格,它永远不会"陈旧".

如图所示,使用得到的集合可以非常容易地显示给用户.给定a DataSource,DataGridView将创建列并填充行.

正确的方式

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

  1. 阅读文件
  2. 解析线条
  3. 将CSV数据映射到类
  4. 将文本转换为正确的数据类型
  5. 创造一个经济的收藏家

除了课程,以上所有内容都可以使用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
Run Code Online (Sandbox Code Playgroud)

两行或三行代码,用于读取,解析和创建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
Run Code Online (Sandbox Code Playgroud)

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

资源


Had*_*adi 5

警告:如果CustomerNameProductName值可以包含逗号 (.ie CustomerName = "Callaway , Mark"),则无法使用该String.Split()方法.并且最好搜索第三方csv解析器,或者你可以使用TextFieldParserClass - > MSDN文章

我的答案是假设缺少的字段总是来自行的右侧,而字段值不包含逗号 (否则@Plutonix答案就是您要查找的内容)

使用此代码,您将能够导入缺少字段的行.

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

Line.Count(Function(c As Char) c = ",")
Run Code Online (Sandbox Code Playgroud)

如果count小于7(8列),则会添加缺失","

 String.PadRight((7 - intCommaCount), ",")
Run Code Online (Sandbox Code Playgroud)

注意:如果缺少逗号,则可以使用左侧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
Run Code Online (Sandbox Code Playgroud)

我使用以下代码从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
Run Code Online (Sandbox Code Playgroud)

注意: 这是一个简单的例子,需要添加错误处理Try... Catch,Null检查