注意:我会先说我知道我可能遗漏了一些非常明显的东西。我在其中一个编码阴霾中,我看不到简单的解决方案。
问题:我在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
)
)
就像我说的那样,我知道这可能是错误的方法,所以我很感谢您提供的任何见解!
答案 0 :(得分:2)
使用可用性而非代码解决此问题。让用户选择分隔符。
但是,由于他们可能不知道哪个制表符分隔,CSV等等,只是向他们展示预览。他们可以从选项中选择,直到输出看起来正确和表格。
然后根据所选格式解析它。
答案 1 :(得分:2)
我将展示一个可能是一个非常好的解决方案的算法,不要把这个问题想象成一个简单的问题,这就像猜测一样,所以这个问题不会有一个完美的解决方案。
应该尝试使用统计数据或其他一些启发式方法来尝试使用99%的好解决方案。我是一名计算机科学家,也是一名开发人员,但这是机器学习或数据科学家给出的近似。
这是:
似乎很复杂,但是算法非常好而且不困难。下面是一个计算示例:
分隔符计数(直方图)
<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 ! */}
}