我该如何检测文本文件中使用的分隔符?

sam*_*miz 20 c# csv asp.net text-parsing

我需要能够解析CSV和TSV文件.我不能依赖用户知道差异,所以我想避免要求用户选择类型.有没有一种简单的方法来检测正在使用哪个分隔符?

一种方法是读取每一行并计算制表符和逗号,并找出哪一行最常用.当然,数据可能包括逗号或制表符,因此说起来容易做起来难.

编辑:这个项目的另一个有趣的方面是,当我阅读它时,我还需要检测文件的模式,因为它可能是众多文件中的一个.这意味着在解析它之前我不会知道有多少个字段.

Tim*_*ker 15

在Python中,csv模块中有一个Sniffer类,可用于猜测给定文件的分隔符和引号字符.它的策略是(引自csv.py的文档字符串):


[首先,查看]包含在两个相同引号(可能的quotechar)之间的文本,这些引号之前和之后是相同的字符(可能的分隔符).例如:

         ,'some text',
Run Code Online (Sandbox Code Playgroud)

获胜最多的引用,与分隔符相同.如果没有quotechar,则无法以这种方式确定分隔符.

在这种情况下,请尝试以下方法:

分隔符在每行上出现相同的次数.但是,由于数据格式错误,可能不会.我们不想要全有或全无的方法,所以我们允许这个数字的微小变化.

  1. 建立每行每个字符频率的表格.
  2. 建立一个频率频率表(元频率?),例如'x在10行中发生5次,在1000行中发生6次,在2行中发生7次'
  3. 使用元频率的模式来确定该 角色的预期频率
  4. 找出角色实际达到目标的频率
  5. 最符合其目标的角色是分隔符

出于性能原因,数据以块的形式进行评估,因此它可以尝试并评估可能的最小部分数据,并根据需要评估其他块.


我不打算在这里引用源代码 - 它位于每个Python安装的Lib目录中.

请记住,CSV也可以使用分号而不是逗号作为分隔符(例如,在德语版本的Excel中,CSV以分号分隔,因为逗号在德国用作小数分隔符...)


dom*_*mer 14

您可以在预览窗口中显示结果 - 类似于Excel的工作方式.在这种情况下使用错误的分隔符时非常清楚.然后,您可以允许他们选择一系列分隔符并实时预览更新.

然后你可以简单地猜测开始时的分隔符(例如,首先是逗号或制表符).

  • 在导入之前向用户显示结果是一个很好的举措我认为,但智能猜测对于使用经验也是很好的.所以组合非常好! (2认同)

Ver*_*mis 6

我遇到了类似的需求,并认为我会分享我的想法。我还没有通过它运行大量数据,因此可能存在边缘情况。此外,请记住,此函数的目标不是 100% 确定分隔符,而是呈现给用户的最佳猜测。

/// <summary>
/// Analyze the given lines of text and try to determine the correct delimiter used. If multiple
/// candidate delimiters are found, the highest frequency delimiter will be returned.
/// </summary>
/// <example>
/// string discoveredDelimiter = DetectDelimiter(dataLines, new char[] { '\t', '|', ',', ':', ';' });
/// </example>
/// <param name="lines">Lines to inspect</param>
/// <param name="delimiters">Delimiters to search for</param>
/// <returns>The most probable delimiter by usage, or null if none found.</returns>
public string DetectDelimiter(IEnumerable<string> lines, IEnumerable<char> delimiters) {
  Dictionary<char, int> delimFrequency = new Dictionary<char, int>();

  // Setup our frequency tracker for given delimiters
  delimiters.ToList().ForEach(curDelim => 
    delimFrequency.Add(curDelim, 0)
  );

  // Get a total sum of all occurrences of each delimiter in the given lines
  delimFrequency.ToList().ForEach(curDelim => 
    delimFrequency[curDelim.Key] = lines.Sum(line => line.Count(p => p == curDelim.Key))
  );

  // Find delimiters that have a frequency evenly divisible by the number of lines
  // (correct & consistent usage) and order them by largest frequency
  var possibleDelimiters = delimFrequency
                    .Where(f => f.Value > 0 && f.Value % lines.Count() == 0)
                    .OrderByDescending(f => f.Value)
                    .ToList();

  // If more than one possible delimiter found, return the most used one
  if (possibleDelimiters.Any()) {
    return possibleDelimiters.First().Key.ToString();
  }
  else {
    return null;
  }   

}
Run Code Online (Sandbox Code Playgroud)