Scala:在CSV文件中自动检测分隔符/分隔符

时间:2014-05-20 11:05:58

标签: scala csv split separator opencsv

我使用OpenCSV库来分割我的CSV文件。现在我需要绝对确定地检测分隔符/分隔符。 我在网上搜索过但我只找到了一些例子,你可以在这些例子中创建一个候选列表并尝试其中一个。我认为这不是最好的方法,因为你可能会遇到错误。 我的分割器应该可以在任何CSV(我无法控制)上正常工作,因此它必须尽可能通用。 有没有人有一个好的解决方案?

3 个答案:

答案 0 :(得分:3)

您可能已经看过这个related SO question,其列出了良好的策略,例如计算潜在分隔符出现的次数,和/或在使用假设分隔符时验证每行具有相同的列数。

不幸的是,绝对确定是不可能的,因为格式不包括在文件中明确指定分隔符的方法。我认为使其尽可能通用的最佳解决方案是让用户指定何时不是逗号(这是opencsv如何处理它),或者如果您或他们确定,可能允许客户指定分隔符自动检测失败。如果这不能互动,那么我认为你能做的最好的事情是记录你认为失败的案例,以便他们以后可以处理。

另外,我认为错误率会低于您的预期。我的猜测是99%的分隔符将是逗号,分号,句点或制表符。我不幸地看到懒惰的编码器使用类似插入符号,管道或波浪形的东西来划分字段,假设数据不包含它,因此它们不必进行适当的转义。但这不是常态,不应该被视为CSV。

Python csv模块有一个Sniffer类,用于猜测分隔符(用户提供候选列表);你可能想看一下它的implementation

答案 1 :(得分:0)

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

这对我有用,但我总是很乐意被展示出更优化的方式。

答案 2 :(得分:0)

How to determine the delimiter in CSV file中,我提到了Univocity-Parsers,这似乎是一个维护良好且流行的库,实际上提供了一个API来为您处理检测。