Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何检测文本文件中使用的分隔符?_C#_Asp.net_Csv_Text Parsing - Fatal编程技术网

C# 如何检测文本文件中使用的分隔符?

C# 如何检测文本文件中使用的分隔符?,c#,asp.net,csv,text-parsing,C#,Asp.net,Csv,Text Parsing,我需要能够解析CSV和TSV文件。我不能依赖用户来了解差异,因此我希望避免要求用户选择类型。是否有一种简单的方法来检测正在使用的分隔符 一种方法是读入每一行并计算制表符和逗号,然后找出哪一行最常用。当然,数据可能包括逗号或制表符,因此说起来容易做起来难 编辑:这个项目另一个有趣的方面是,当我读入文件时,我还需要检测文件的模式,因为它可能是许多模式之一。这意味着在我能够解析它之前,我不知道我有多少字段。你知道每行应该有多少字段吗?如果是这样的话,我会阅读文件的前几行,并根据它进行检查 根据我的经验

我需要能够解析CSV和TSV文件。我不能依赖用户来了解差异,因此我希望避免要求用户选择类型。是否有一种简单的方法来检测正在使用的分隔符

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


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

你知道每行应该有多少字段吗?如果是这样的话,我会阅读文件的前几行,并根据它进行检查

根据我的经验,“普通”数据通常包含逗号,但很少包含制表符。这将建议您检查前几行中的制表符数量是否一致,并将该选项作为首选猜测。当然,这完全取决于你得到了什么样的数据


最终,很可能会有一个对这两种格式都完全有效的文件——所以你不能让它绝对万无一失。这必须是一项“尽力而为”的工作。

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


然后,您可以简单地猜测要使用的分隔符(例如,逗号或制表符排在第一位)。

没有“有效”的方法。

我认为您建议的解决方案是最好的方法。在格式良好的CSV或TSV文件中,每行的逗号或制表符数量应保持不变(完全没有变化)。对文件的每一行进行计数,并检查哪一行对所有行都是常量。每一行的两个delimeter的计数似乎不太可能相同,但在这种不可思议的罕见情况下,您当然可以提示用户


如果制表符和逗号的数量都不是常数,则向用户显示一条消息,告诉他们文件格式不正确,但程序认为它是一个(任何格式的每行delimeters的标准偏差最低)文件。

假设每行有固定数量的字段,并且值中的任何逗号或制表符都用引号括起来(“),您应该能够计算出每行中每个字符的频率。如果字段不固定,这将更加困难,如果不使用引号括起其他分隔字符,我怀疑这几乎是不可能的(取决于数据,特定于区域设置)根据我的经验,数据很少包含制表符,所以一行制表符分隔的字段(通常)是相当明显的

不过逗号更难,尤其是在非美国地区读取数据时。如果读取在国外生成的文件,数字数据可能包含大量逗号,因为浮点数通常会包含它们


但最后,唯一安全的方法通常是尝试,然后将其呈现给用户,并允许他们进行调整,尤其是当您的数据包含逗号和/或制表符时。

我假设在普通文本中,制表符非常罕见,除了第一个字符在一行中——想想缩进的段落或源代码。我想如果你找到嵌入的标签(即不带逗号的标签),您可以假设制表符被用作分隔符,并且大多数情况下都是正确的。这只是一种预感,没有经过任何研究验证。当然,我会让用户选择覆盖自动计算模式。

只要读几行,计算逗号数和制表符数,然后比较它们。如果有20个逗号没有制表符,它是CSV格式的。如果有20个制表符和2个逗号(可能在数据中),则它是TSV格式的。

假设您有一组您期望的标准列

我会使用FileHelper(SourceForge上的开源项目)。

定义两个阅读器模板,一个用于comas,一个用于选项卡


如果第一个失败,请尝试第二个。

在Python中,csv模块中有一个嗅探器类,可用于猜测给定文件的分隔符和引号字符。其策略是(引用自csv.py的docstrings):


[首先,查找]两个相同引号之间的文本 (可能的引语)前面和后面的引语 使用相同的字符(可能的分隔符)。 例如:

         ,'some text',
赢得最多的报价,与分隔符相同。 如果没有引号,则无法确定分隔符 这边走

在这种情况下,请尝试以下操作:

分隔符在上出现的次数应相同 每行。但是,由于数据格式不正确,它可能不会。我们不希望 一种全有或全无的方法,因此我们允许在这方面有小的变化 号码

  • 建立一个频率表 每行上的每个字符
  • 建立一个关于这个频率的表格 频率(元频率?),例如。 'x在10行中出现了5次,6 1000行中的次数,2行中的7次 排
  • 使用元频率模式 确定预期的 那个角色的频率
  • 找出角色的频率 实际上达到了这个目标
  • 最符合其角色的角色 目标是分隔符
  • 出于性能方面的原因,数据是按块计算的,因此可以 尝试并评估尽可能小的数据部分,评估 根据需要添加块


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

    请记住,CSV也可以使用分号而不是逗号作为分隔符(例如德语版本)
    $csv = 'something;something;something
    someotherthing;someotherthing;someotherthing
    ';
    $candidates = array(',', ';', "\t");
    $csvlines = explode("\n", $csv);
    foreach ($candidates as $candidatekey => $candidate) {
     $lastcnt = 0;
     foreach ($csvlines as $csvline) {
      if (strlen($csvline) <= 2) continue;
      $thiscnt = substr_count($csvline, $candidate);
      if (($thiscnt == 0) || ($thiscnt != $lastcnt) && ($lastcnt != 0)) {
       unset($candidates[$candidatekey]);
       break;
      }
      $lastcnt = $thiscnt;
     }
    }
    $delim = array_shift($candidates);
    echo $delim;
    
    while ((line = readFile.ReadLine()) != null)
    {
        if (line.Split('\t').Length > line.Split(',').Length) // tab delimited or comma delimited?
            row = line.Split('\t');
        else
            row = line.Split(',');
    
        parsedData.Add(row);
    }
    
    /// <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;
      }   
    
    }