繁体   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