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

6

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


3
抱怨模式开启:有趣的是,CSV代表逗号分隔值。 - om-nom-nom
@om-nom-nom 如果一直都是这样就太好了。但现实是不同的。 - YoBre
@om-nom-nom:有人说C代表字符。 - Tobias Brandt
3个回答

5

您可能已经看到了这个相关SO问题,其中列出了好的策略,例如计算潜在分隔符出现的次数,以及/或者验证使用假定分隔符时每行是否具有相同数量的列。

不幸的是,绝对的确定性是不可能的,因为格式没有包括一种在文件中明确指定分隔符的方式。我认为使其尽可能通用的最佳解决方案是当它不是逗号(这是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

这对我来说可行,但我总是乐意看到更好、更优化的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接