如何parsing包含数据中的换行符的Excel CSV数据?

我试图使用PHPparsing一组CSV数据,但有一个主要问题。 其中一个字段是一个长描述字段,其本身包含shell内的换行符。

我的主要问题是编写一段代码,可以逐行拆分数据,但也可以识别不应该使用数据中的换行符。 在这个领域内的换行符没有正确逃脱,使他们​​很难区分合法的线路中断。

我试图想出一个正确的expression式,可以正确处理它,但迄今还没有运气。 有任何想法吗?

CSV格式:

"####","text data here", "text data \n with linebreaks \n here"\n "####","more text data", "more data \n with \n linebreaks \n here"\n 

根据aleske,PHP的fgetcsv函数文档中的一位评论者:

PHP的CSV处理的东西是非标准的,与RFC4180相矛盾,因此fgetcsv()不能正确处理文件[包含换行符] …

他提出了以下function来解决这个限制:

 function csvstring_to_array(&$string, $CSV_SEPARATOR = ';', $CSV_ENCLOSURE = '"', $CSV_LINEBREAK = "\n") { $o = array(); $cnt = strlen($string); $esc = false; $escesc = false; $num = 0; $i = 0; while ($i < $cnt) { $s = $string[$i]; if ($s == $CSV_LINEBREAK) { if ($esc) { $o[$num] .= $s; } else { $i++; break; } } elseif ($s == $CSV_SEPARATOR) { if ($esc) { $o[$num] .= $s; } else { $num++; $esc = false; $escesc = false; } } elseif ($s == $CSV_ENCLOSURE) { if ($escesc) { $o[$num] .= $CSV_ENCLOSURE; $escesc = false; } if ($esc) { $esc = false; $escesc = true; } else { $esc = true; $escesc = false; } } else { if ($escesc) { $o[$num] .= $CSV_ENCLOSURE; $escesc = false; } $o[$num] .= $s; } $i++; } // $string = substr($string, $i); return $o; } 

这看起来会做的伎俩。

我发现在将CSV转换为unix格式后,您可以使用正常的CSVparsing器。

这是一个function,为我做了伎俩。

 function dos2unix($s) { $s = str_replace("\r\n", "\n", $s); $s = str_replace("\r", "\n", $s); $s = preg_replace("/\n{2,}/", "\n\n", $s); return $s; } 

还有一个parsing函数

 function csvstring_to_array($string, $separatorChar = ',', $enclosureChar = '"', $newlineChar = PHP_EOL) { // @author: Klemen Nagode $string = dos2unix($string); $array = array(); $size = strlen($string); $columnIndex = 0; $rowIndex = 0; $fieldValue=""; $isEnclosured = false; for($i=0; $i<$size;$i++) { $char = $string{$i}; $addChar = ""; if($isEnclosured) { if($char==$enclosureChar) { if($i+1<$size && $string{$i+1}==$enclosureChar){ // escaped char $addChar=$char; $i++; // dont check next char }else{ $isEnclosured = false; } }else { $addChar=$char; } }else { if($char==$enclosureChar) { $isEnclosured = true; }else { if($char==$separatorChar) { $array[$rowIndex][$columnIndex] = $fieldValue; $fieldValue=""; $columnIndex++; }elseif($char==$newlineChar) { echo $char; $array[$rowIndex][$columnIndex] = $fieldValue; $fieldValue=""; $columnIndex=0; $rowIndex++; }else { $addChar=$char; } } } if($addChar!=""){ $fieldValue.=$addChar; } } if($fieldValue) { // save last field $array[$rowIndex][$columnIndex] = $fieldValue; } return $array; } 

问题是,“\ n”转义string不会计算为Excel用于其行分隔符的相同的新行字符。 Excel使用的ASCII字符是ASCII 13.以下代码将有效地parsing通过$ file_get_contents()方法传入的.csv文件。

 <?php //variable to store filename of file $filename = $_SERVER['DOCUMENT_ROOT'] . "/site/docs/boothmap.csv"; //read file in as string $file = file_get_contents($filename); //convert csv to array //first to single dimensional array $array1D = explode(chr(13),$file); //create new array to hold 2d array $array2D = array(); //iterate through 1 dimensional array and explode each value to the new array foreach($array1D as &$row) { array_push($array2D, explode(',',$row)); } //pop off empty last row of array2D array_pop($array2D); //iterate through $array2D building table of data //start table with column headers echo "<table border=\"1\">\n<tr>\n<th>Company</th>\n<th>Booth #</th>\n<th>Location</th>\n</tr>\n"; foreach ($array2D as &$row) { echo "<tr>\n"; foreach($row as &$subrow) { echo "<td>" . $subrow . "</td>\n"; } echo "</tr>\n"; } //close table echo "</table>"; 

我结束了能够修改正则expression式与某些特殊的标志,以满足我的需求。 我用了下面的函数调用:

 preg_match_all('/"\d+",".*",".*"\n/sU', $csv_data, $matches); 

这似乎有几个原因:

1)“s”标志告诉编辑在网点下面捕捉换行符,通常不是这种情况。 这个不幸的副作用是合法的换行符也被这个点所捕获,理论上可以将整个CSV匹配到一个结果,所以

2)我添加了U标志。 这表示默认情况下,这个点是不可理解的,因此,它现在只匹配一条线。

这是一个古老的线程,但我遇到了这个问题,我用一个正则expression式来解决它,所以你可以避免一个库。 这里的代码是在PHP中,但它可以适应其他语言。

$parsedCSV = preg_replace('/(,|\n|^)"(?:([^\n"]*)\n([^\n"]*))*"/', '$1"$2 $3"', $parsedCSV);

如果内容太大,可能效率不高,但可以帮助很多情况,这个想法可以重复使用,也许可以通过对较小的块进行优化(但是您需要使用固定大小的缓冲区来处理切割) 。 这个解决scheme假设包含换行符的字段被双引号括起来,这似乎是一个有效的假设,至less对于我目前看到的。 而且,双引号应该跟在a之后,或者放在新行(或第一行)的开头。

例:

field1,"field2-part1\nfield2-part2",field3

这里\ n被空白符replace,结果是:

field1,"field2-part1 field2-part2",field3

正则expression式也应该处理多个换行符。

你可以使用fgetcsv或者strgetcsv来parsing一个csv。 看看php文档里面的例子。