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

问题描述 投票:2回答:3

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

scala csv split separator opencsv
3个回答
3
投票

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

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

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

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


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

这对我有用,但我总是很高兴看到更好的更优化的方式。


0
投票

How to determine the delimiter in CSV file,我发现提到了Univocity-Parsers,它看起来像一个维护良好且受欢迎的库,它实际上提供了一个API来处理你的检测。

© www.soinside.com 2019 - 2024. All rights reserved.