PHP - 检测CSV分隔符的最佳方法

sim*_*www 7 php csv delimiter

我已经看到多个线程,关于自动检测传入CSV的分隔符的最佳解决方案.其中大多数是长度在20-30行之间的函数,多个循环预定的分隔符列表,读取前5行和匹配计数等等

这是一个例子

我刚刚实施了这个程序,只做了一些修改.工作出色.

然后我找到了以下代码:

private function DetectDelimiter($fh)
{
    $data_1 = null;
    $data_2 = null;
    $delimiter = self::$delim_list['comma'];
    foreach(self::$delim_list as $key=>$value)
    {
        $data_1 = fgetcsv($fh, 4096, $value);
        $delimiter = sizeof($data_1) > sizeof($data_2) ? $key : $delimiter;
        $data_2 = $data_1;
    }

    $this->SetDelimiter($delimiter);
    return $delimiter;
}
Run Code Online (Sandbox Code Playgroud)

这对我来说看起来像是在实现SAME结果,其中$ delim_list是一个分隔符数组,如下所示:

static protected $delim_list = array('tab'=>"\t", 
                                     'semicolon'=>";", 
                                     'pipe'=>"|", 
                                     'comma'=>",");
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么我不应该这么简单地做到这一点,为什么到处都是我看起来更复杂的解决方案似乎是公认的答案?

谢谢!

Ahm*_*awy 10

这个功能很优雅:)

/**
* @param string $csvFile Path to the CSV file
* @return string Delimiter
*/
public function detectDelimiter($csvFile)
{
    $delimiters = [";" => 0, "," => 0, "\t" => 0, "|" => 0];

    $handle = fopen($csvFile, "r");
    $firstLine = fgets($handle);
    fclose($handle); 
    foreach ($delimiters as $delimiter => &$count) {
        $count = count(str_getcsv($firstLine, $delimiter));
    }

    return array_search(max($delimiters), $delimiters);
}
Run Code Online (Sandbox Code Playgroud)

  • 要检测是否未找到分隔符,可以在 return 语句之前添加: `if( array_sum( $delimiters ) <= count( $delimiters ) ) return false;` (2认同)
  • @Braza 在单行中,如果测试了一些极端的边缘情况,可能真的很难检测到它的可靠性。我创建了一个实用程序类,它检查每一行并返回整个文件或至少几行的结果。我在这里使用了此页面中的一些代码。您可以在这里查看:https://gist.github.com/DavidBruchmann/1215dc4fb9b7bd339253de5b6e304909 (2认同)

小智 5

固定版本。

在您的代码中,如果一个字符串包含多个定界符,则会得到错误的结果(例如:val;字符串,带逗号; val2; val3)。同样,如果文件有1行(行数<分隔符数)。

这是一个固定的变体:

private function detectDelimiter($fh)
{
    $delimiters = ["\t", ";", "|", ","];
    $data_1 = null; $data_2 = null;
    $delimiter = $delimiters[0];
    foreach($delimiters as $d) {
        $data_1 = fgetcsv($fh, 4096, $d);
        if(sizeof($data_1) > sizeof($data_2)) {
            $delimiter = $d;
            $data_2 = $data_1;
        }
        rewind($fh);
    }

    return $delimiter;
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*mes 5

这些都不适用于我的用例。所以我做了一些细微的修改。

   /**
    * @param string $filePath
    * @param int $checkLines
    * @return string
    */
   public function getCsvDelimiter(string $filePath, int $checkLines = 3): string
   {
      $delimiters =[",", ";", "\t"];

      $default =",";

       $fileObject = new \SplFileObject($filePath);
       $results = [];
       $counter = 0;
       while ($fileObject->valid() && $counter <= $checkLines) {
           $line = $fileObject->fgets();
           foreach ($delimiters as $delimiter) {
               $fields = explode($delimiter, $line);
               $totalFields = count($fields);
               if ($totalFields > 1) {
                   if (!empty($results[$delimiter])) {
                       $results[$delimiter] += $totalFields;
                   } else {
                       $results[$delimiter] = $totalFields;
                   }
               }
           }
           $counter++;
       }
       if (!empty($results)) {
           $results = array_keys($results, max($results));

           return $results[0];
       }
return $default;
}

Run Code Online (Sandbox Code Playgroud)


and*_*ndy 2

通常,您无法检测文本文件的分隔符。如果有其他提示,您需要在检测中实现它们以确保确定。

建议的方法的一个特殊问题是,它将计算文件不同行中的元素数量。假设你有一个这样的文件:

a;b;c;d
a   b;  c   d
this|that;here|there
It's not ready, yet.; We have to wait for peter, paul, and mary.; They will know what to do
Run Code Online (Sandbox Code Playgroud)

尽管这似乎是用分号分隔的,但您的方法将返回comma.

  • 确实如此,这正是我的观点:如果您想正确读取 csv,请要求用户指定分隔符。 (2认同)