[英]Scala: Auto detection of delimiter/separator in CSV file
我正在使用OpenCSV庫拆分CSV文件。 現在,我需要絕對確定地檢測定界符/分隔符。 我在網上搜索過,但只找到了一些示例,在這些示例中,您創建了候選人列表並嘗試其中的一個。 我認為這不是最好的方法,因為您可能會出錯。 我的分割器應該可以在任何CSV(我無法控制)上正常工作,因此它必須盡可能通用。 有沒有人有一個好的解決方案?
您可能已經看到了這個相關的SO問題 ,其中列出了不錯的策略,例如計算可能的分隔符出現的次數,和/或在使用假設的分隔符時驗證每一行具有相同的列數。
不幸的是,絕對確定性是不可能的,因為格式不包括在文件中明確指定分隔符的方法。 我認為使它盡可能通用的最佳解決方案是讓用戶指定何時不是逗號(這是opencsv對其進行處理的方式),或者可能是允許客戶端指定分隔符(如果您或他們確定)自動檢測失敗。 如果這不是交互式的,那么我認為您能做的最好是記錄您認為失敗的案例,以便他們稍后進行處理。
另外,我認為錯誤率將低於您的預期。 我的猜測是,定界符有99%的時間是逗號,分號,句點或制表符。 不幸的是,我已經看到懶惰的編碼器使用插入符,豎線或代字號之類的東西來分隔字段,並假設數據將不包含它們,因此他們不必進行適當的轉義。 但這不是規范,不應將其視為CSV。
我最近一直在研究CSV文件的分隔符/分隔符檢測問題。 我提出了以下建議,希望對其他人有所幫助,也許會得到反饋以改進。
我的解決方案基於我已經閱讀過的有關該問題的幾篇文章。 因為對字段定界符沒有限制,所以我決定使用ASCII表並消除明顯的(字母數字字符)和不太明顯的(不可打印的字符)(TAB代碼除外)。 使用這些值,我填充了一個字典,其中ASCII碼是要填充我的代碼的值的鍵。
然后,需要逐行讀取CSV,在每行中查找是否存在任何字典鍵字符,然后遞增我遇到的每個字典鍵的值。 在此示例中,循環將繼續到文件末尾或限制100次。 您可以根據自己的喜好更改此設置,但100遠遠不足以檢測到分隔符。 然后,由字典鍵(ASCII碼)確定最大值的分隔符。
調用例程示例
private sub Main()
dim separator As Char
separator= separatorDetect(txtInputFile.Text)
end sub
主要檢測功能
Private Function separatorDetect(ByVal StrFileName As String) As Char
Dim i As Int16 = 0
Dim separator As List(Of Char)
Dim dictSeparators As New Dictionary(Of Integer, Integer)
dictSeparators.Add(9, 0)
dictSeparators.Add(33, 0)
For i = 35 To 47
dictSeparators.Add(i, 0)
Next
For i = 91 To 96
dictSeparators.Add(i, 0)
Next
For i = 123 To 126
dictSeparators.Add(i, 0)
Next
Dim lineCounter As Integer = 0
Dim line As String = String.Empty
Dim keyList As New List(Of Integer)
For Each key In dictSeparators.Keys
keyList.Add(key)
Next
Dim tmp As Char
Using textReader = New StreamReader(StrFileName)
Do Until textReader.EndOfStream
line = textReader.ReadLine.Trim
For Each key In keyList
tmp = Convert.ToChar(key)
dictSeparators.Item(key) = dictSeparators.Item(key) + InStrCount(line, tmp)
Next
lineCounter += 1
If lineCounter = 99 Then GoTo readEnd
Loop
End Using
readEnd:
Dim max = dictSeparators.Aggregate(Function(l, r) If(l.Value > r.Value, l, r)).Key
Return Chr(max)
End Function
遞歸索引計數功能
Private Function InStrCount(ByVal SourceString As String, ByVal SearchString As Char, _
Optional ByRef StartPos As Integer = 0, _
Optional ByRef Count As Integer = 0) As Integer
If SourceString.IndexOf(SearchString, StartPos) > -1 Then
Count += 1
InStrCount(SourceString, SearchString, SourceString.IndexOf(SearchString, StartPos) + 1, Count)
End If
Return Count
End Function
這對我有用,但是我總是很高興能被展示給我一種更好的,更優化的方式。
在如何確定CSV文件中的定界符中,我提到了Univocity-Parsers ,它似乎是一個維護良好且流行的庫,實際上提供了可為您處理檢測的API。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.