簡體   English   中英

Scala:自動檢測CSV文件中的定界符/分隔符

[英]Scala: Auto detection of delimiter/separator in CSV file

我正在使用OpenCSV庫拆分CSV文件。 現在,我需要絕對確定地檢測定界符/分隔符。 我在網上搜索過,但只找到了一些示例,在這些示例中,您創建了候選人列表並嘗試其中的一個。 我認為這不是最好的方法,因為您可能會出錯。 我的分割器應該可以在任何CSV(我無法控制)上正常工作,因此它必須盡可能通用。 有沒有人有一個好的解決方案?

您可能已經看到了這個相關的SO問題 ,其中列出了不錯的策略,例如計算可能的分隔符出現的次數,和/或在使用假設的分隔符時驗證每一行具有相同的列數。

不幸的是,絕對確定性是不可能的,因為格式不包括在文件中明確指定分隔符的方法。 我認為使它盡可能通用的最佳解決方案是讓用戶指定何時不是逗號(這是opencsv對其進行處理的方式),或者可能是允許客戶端指定分隔符(如果您或他們確定)自動檢測失敗。 如果這不是交互式的,那么我認為您能做的最好是記錄您認為失敗的案例,以便他們稍后進行處理。

另外,我認為錯誤率將低於您的預期。 我的猜測是,定界符有99%的時間是逗號,分號,句點或制表符。 不幸的是,我已經看到懶惰的編碼器使用插入符,豎線或代字號之類的東西來分隔字段,並假設數據將不包含它們,因此他們不必進行適當的轉義。 但這不是規范,不應將其視為CSV。

Python csv模塊有一個Sniffer類,它猜測定界符(用戶提供了候選列表)。 您可能需要看一下它的實現

我最近一直在研究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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM