3

注意:我首先要说我知道我可能遗漏了一些非常明显的东西。我正处于看不到简单解决方案的编码迷雾中。

问题:我用 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
    )
)

就像我说的,我知道这可能是处理这个问题的错误方法,所以我很感谢你能提供的任何见解!

4

5 回答 5

3

用可用性而不是代码来解决这个问题。让用户选择分隔符。

但是,由于他们可能不知道 CSV 等人用什么制表符分隔,所以只给他们看一个预览。他们可以从选项中进行选择,直到输出看起来正确且呈表格状。

然后根据选择的格式对其进行解析。

于 2013-02-19T02:20:59.567 回答
2

我将展示一个可能是一个很好的解决方案的算法,不要认为这个问题很容易,这就像猜测,所以这个问题不会有一个完美的解决方案。

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

这里是:

  1. 从文件的所有行中选择一些随机行,比如 10
  2. 计算每个分隔符候选的出现次数
  3. 用这个数字计算每个分隔符的平均值和方差。
  4. 标准化数字,这意味着使用自定义线性函数给出 0 到 1 之间的数字
  5. 为每个分隔符和总和的两个值赋予权重,这给出了每个分隔符的分数,您可以将其用作决策

看起来很复杂,但它是一个相当不错且不难的算法。下面是一个计算示例:

分隔符计数(直方图)

|         | ; | , | \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 |

计算和最终分数

|            |  ;   |  ,   |  \t  |  | WEIGHTS |  ;   |  ,   | \t |
|------------|------|------|------|--|---------|------|------|----|
| AVERAGE    |  2,7 |    1 | 15,3 |  |         |      |      |    |
| NORMALIZED | 0,17 | 0,06 |    1 |  | 1       | 0,17 | 0,06 |  1 |
| VARIANCE   | 0,21 |    0 | 1335 |  |         |      |      |    |
| NORMALIZED | 0,99 |    1 |    0 |  | 3       | 2,99 |    3 |  0 |
|            |      |      |      |  | SCORE   | 3,17 | 3,06 |  1 |

如您所见,分隔符';' 有更好的分数。我认为加权方差比发现的分隔符的平均值也好。更有可能有一个文件,其中分隔符在每行中变化不大。

于 2018-03-27T10:00:43.853 回答
1

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

不再尝试解析为 CSV,而是尝试检索有效的电子邮件地址。我不认为空格、逗号、制表符或换行符是有效的电子邮件部分,对吧?(谁知道;)查看这个关于使用正则表达式验证电子邮件的讨论- 这样您就可以看到这个解决方案的一些缺陷。

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

祝你好运!

于 2013-02-19T02:14:00.030 回答
1

SplFileObject::getCsvControl在手册中

我直到为时已晚才找到它,所以写了一个运行良好的函数。如果它有用/感兴趣,我的方法是:

我使用$handle$ColName参数$ColName可选

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

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

fgetcsv 似乎在块中工作,并强制每条记录具有与块中的最大值相同的字段数,因此即使每条记录的字段数量不同,这也适用

于 2015-04-29T06:30:12.357 回答
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 ! */}
}
于 2014-08-12T13:23:54.227 回答