Dot-Net

讀取 CSV 文件一些缺失的列

  • January 4, 2021

我正在嘗試使用以下程式碼將 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)在循環內讀取時,它會返回

指數數組的邊界之外

您應該掌握的一件事是,這些Filexxxx方法幾乎已被正式和正式棄用。使用它們時,Intellisense 會彈出:

…與 FileOpen 相比,我的功能在文件 I/O 操作中為您提供更好的生產力和性能。有關詳細資訊,請參閱 Microsoft.VisualBasic.FileIO.FileSystem。

他們在談論,My.Computer.FileSystem但還有一些更有用的 NET 方法。

該文章沒有透露數據將如何儲存,但如果它是任何類型和/或結構的數組,那麼如果不是過時的話,這些至少是次優的。這會將它儲存在一個類中,以便可以將數字數據儲存為數字,並且List將使用 a 代替數組。

我用一些隨機數據製作了一個類似於你的快速文件{"CustName", "Phone", "UserName", "Product", "Cost", "Price", "Profit", "SaleDate", "RefCode"}

  • CustName 有 70% 的時間存在
  • 使用者名永遠不存在
  • RefCode 存在 30% 的時間
  • 我添加了一個SaleDate來說明數據轉換

Ziggy Aurantium,132-5562,,貓糧,8.26,9.95,1.69,08/04/2016,

Catrina Caison,899-8599,,磨刀器,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

結果: 在此處輸入圖像描述

注意

儲存可以簡單計算的東西通常是個壞主意。所以Profit屬性是:

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

如果更新成本或價格,它永遠不會“過時”。

如圖所示,使用生成的集合可以很容易地向使用者顯示。給定 a DataSourceDataGridView將創建列並填充行。

正確的方式

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

兩三行程式碼來讀取、解析和創建 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

這會將欄位一一返回給您。它不會轉換任何日期或價格,但也不會因失去的數據元素而窒息。

資源

引用自:https://stackoverflow.com/questions/41108845