在PHP中确定.csv分隔符

时间:2013-02-19 00:54:16

标签: php parsing csv delimiter fgetcsv

注意:我会先说我知道我可能遗漏了一些非常明显的东西。我在其中一个编码阴霾中,我看不到简单的解决方案。

问题:我在PHP中编写了一个脚本来解析.csv文件,选择包含电子邮件地址的列,并将它们放入数据库中。现在,我发现用户正在尝试上传具有.csv文件类型但实际上不以逗号分隔的文件。我正在尝试编写一个能够正确确定分隔符(制表符,新行,空格等)的函数,但是遇到了一些问题。我想我想获得所有这些地址的数组,这样键的数量就会增加该分隔符的可信度。

代码:

$filename = "../some/path/test.csv";   
if (($handle = fopen($fileName, "r")) !== FALSE) {
    $delimiters = array(',', ' ', "\t", "\n");
    $delimNum = 0;
    foreach ($delimiters as $delimiter) {
      $row = 0;
      while (($data = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
        $data = (string)$data[0];
        $delimiterList[$delimNum] = explode($delimiter, $data);
        $row++;
    }
    $delimNum++;
}
die(print_r($delimiterList));
}

结果:

Array
(
[0] => Array
    (
        [0] => email
peter.parker@example.com
atticus.finch@example.com
steve.rogers@example.com
phileas.fogg@example.com
s.winston@example.com
paul.revere@example.com
fscott.fitzgerald@example.com
jules.verne@example.com
martin.luther@example.com
ulysses.grant@example.com
tony.stark@example.com
    )
)

就像我说的那样,我知道这可能是错误的方法,所以我很感谢您提供的任何见解!

5 个答案:

答案 0 :(得分:2)

使用可用性而非代码解决此问题。让用户选择分隔符。

但是,由于他们可能不知道哪个制表符分隔,CSV等等,只是向他们展示预览。他们可以从选项中选择,直到输出看起来正确和表格。

然后根据所选格式解析它。

答案 1 :(得分:2)

我将展示一个可能是一个非常好的解决方案的算法,不要把这个问题想象成一个简单的问题,这就像猜测一样,所以这个问题不会有一个完美的解决方案。

应该尝试使用统计数据或其他一些启发式方法来尝试使用99%的好解决方案。我是一名计算机科学家,也是一名开发人员,但这是机器学习或数据科学家给出的近似。

这是:

  1. 选择文件所有行的多个随机行,例如10
  2. 计算每个分隔符候选项的出现次数
  3. 使用此数字计算每个分隔符的平均值和方差。
  4. 标准化数字,这意味着给出介于0和1之间的数字using your custom linear function
  5. weigths to the two values for each delimiters and sum,这会给出每个分隔符的分数,您可以将其用作决定
  6. 似乎很复杂,但是算法非常好而且不困难。下面是一个计算示例:

    分隔符计数(直方图)

    <script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/modules/exporting.js"></script>
    
    <div id="container" style="min-width: 310px; max-width: 800px; height: 400px; margin: 0 auto"></div>

    计算和最终得分

    |         | ; | , | \t  |
    |---------|---|---|-----|
    | LINE 1  | 3 | 1 |  13 |
    | LINE 2  | 2 | 1 |   0 |
    | LINE 3  | 3 | 1 |   0 |
    | LINE 4  | 3 | 1 | 124 |
    | LINE 5  | 2 | 1 |   2 |
    | LINE 6  | 2 | 1 |   2 |
    | LINE 7  | 3 | 1 |  12 |
    | LINE 8  | 3 | 1 |   0 |
    | LINE 9  | 3 | 1 |   0 |
    | LINE 10 | 3 | 1 |   0 |
    

    正如您所见,分隔符&#39;;&#39;得分更高。我认为,除了找到的分隔符的平均值之外,对方差的权重也更大。它更可能有一个文件,其中分隔符在每行中变化不大。

答案 2 :(得分:1)

这不是一个完美的解决方案,但它可能会帮助你 - 如果你不能问分隔符是什么。

不要再尝试解析为CSV,只需检索有效的电子邮件地址即可。我不认为空格,逗号,制表符或换行符是有效的电子邮件部分吗? (谁知道;)查看关于using regular expressions to validate email的讨论 - 所以你可以看到这个解决方案的一些缺陷。

但是,然后我会使用preg_match_all()编写正则表达式,并以有效的电子邮件格式检索所有字符串的列表。

祝你好运!

答案 3 :(得分:1)

手册中

SplFileObject::getCsvControl

我找不到它为时已晚,所以写了一个效果很好的函数。 如果它有用/有意义,我的方法是:

我将$handle$ColName参数与$ColName可选

一起使用

$ ColName允许您检查哪个分隔符在第一个记录中找到了预期的标题列名称,如果csv文件有标题行。

如果没有标题行,或者您不知道列名,则它会转到默认检查:哪个分隔符找到同一记录的大多数字段(这通常是正确的)。然后我还检查该分隔符为接下来的几行返回相同数量的字段。

fgetcsv似乎在块中工作并强制每个记录与块中的max具有相同数量的字段,因此即使每个记录的字段数不同,这也会起作用

答案 4 :(得分:0)

这是我的解决方案。 如果你知道你期望的列数,它的工作原理。 最后,分隔符是$ actual_separation_character

$separator_1=",";
$separator_2=";";
$separator_3="\t";
$separator_4=":";
$separator_5="|";

$separator_1_number=0;
$separator_2_number=0;
$separator_3_number=0;
$separator_4_number=0;
$separator_5_number=0;

/* YOU NEED TO CHANGE THIS VARIABLE */
// Expected number of separation character ( 3 colums ==> 2 sepearation caharacter / row )
$expected_separation_character_number=2;  


$file = fopen("upload/filename.csv","r");
while(! feof($file)) //read file rows
{
    $row= fgets($file);

    $row_1_replace=str_replace($separator_1,"",$row);
    $row_1_length=strlen($row)-strlen($row_1_replace);

    if(($row_1_length==$expected_separation_character_number)or($expected_separation_character_number==0)){
    $separator_1_number=$separator_1_number+$row_1_length;
    }

    $row_2_replace=str_replace($separator_2,"",$row);
    $row_2_length=strlen($row)-strlen($row_2_replace);

    if(($row_2_length==$expected_separation_character_number)or($expected_separation_character_number==0)){
    $separator_2_number=$separator_2_number+$row_2_length;
    }

    $row_3_replace=str_replace($separator_3,"",$row);
    $row_3_length=strlen($row)-strlen($row_3_replace);

    if(($row_3_length==$expected_separation_character_number)or($expected_separation_character_number==0)){
    $separator_3_number=$separator_3_number+$row_3_length;
    }

    $row_4_replace=str_replace($separator_4,"",$row);
    $row_4_length=strlen($row)-strlen($row_4_replace);

    if(($row_4_length==$expected_separation_character_number)or($expected_separation_character_number==0)){
    $separator_4_number=$separator_4_number+$row_4_length;
    }

    $row_5_replace=str_replace($separator_5,"",$row);
    $row_5_length=strlen($row)-strlen($row_5_replace);

    if(($row_5_length==$expected_separation_character_number)or($expected_separation_character_number==0)){
    $separator_5_number=$separator_5_number+$row_5_length;
    }

} // while(! feof($file))  END
fclose($file);

/* THE FILE ACTUAL SEPARATOR (delimiter) CHARACTER */
/* $actual_separation_character */

if ($separator_1_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_1;}
else if ($separator_2_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_2;}
else if ($separator_3_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_3;}
else if ($separator_4_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_4;}
else if ($separator_5_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_5;}
else {$actual_separation_character=";";}

/* 
if the number of columns more than what you expect, do something ...
*/

if ($expected_separation_character_number>0){
if ($separator_1_number==0 and $separator_2_number==0 and $separator_3_number==0 and $separator_4_number==0 and $separator_5_number==0){/* do something ! more columns than expected ! */}
}