Php str_getcsv分隔行删除行中的封闭字符
Php str_getcsv分隔行删除行中的封闭字符,php,csv,parsing,Php,Csv,Parsing,str_getcsv()有一些奇怪的行为。它删除与封闭字符匹配的所有字符,而不仅仅是封闭字符。我试图分两步解析CSV字符串(上传文件的内容): 将CSV字符串拆分为行数组 将每行拆分为字段数组 使用此代码: $whole_file_string = file_get_contents($file); $array_of_lines = str_getcsv ($whole_file_string, "\n", "\""); // step 1. split csv into lines fore
str_getcsv()
有一些奇怪的行为。它删除与封闭字符匹配的所有字符,而不仅仅是封闭字符。我试图分两步解析CSV字符串(上传文件的内容):
$whole_file_string = file_get_contents($file);
$array_of_lines = str_getcsv ($whole_file_string, "\n", "\""); // step 1. split csv into lines
foreach ($array_of_lines as $one_line_string) {
$splitted_line = str_getcsv ($one_line_string, ",", "\""); // step 2. split line into fields
};
在代码示例中,为了示例的清晰性,未对$splitted_line
执行任何操作
然后,我为这个脚本提供一个包含以下内容的文件:“text,with,delimiter”,secondfield
。
执行步骤1时,$array\u行的第一个(也是唯一一个)元素是文本,带分隔符,第二个字段
。因此,当执行步骤2时,它会将该行拆分为4个字段,但需要为2
我不能使用fgetcsv()
,因为在读取文件之后,在步骤1中将其拆分为行之前,会进行一些字符串转换(检查BOM,相应地转换编码等等)
我正在编写自己的字符串解析器(对于CSV格式来说并不复杂),但在编写之前,我想确保这是最好的方法。我有点失望,PHP函数让我对这个简单(我想是很常见的)用例感到失望:用不同的编码处理上传的csv文件
有什么提示吗?您应该一次只调用一行,而不是整个文件
$array_of_lines = file($file, FILE_IGNORE_NEW_LINES); // split CSV into lines
foreach($array_of_lines as $one_line_string) {
$splitted_line = str_getcsv($one_line_string, ",", "\""); // split line into fields
}
这是我自己的CSV解析器,完全符合IETF rfc 4180。
我很想知道这是否可以用正则表达式实现,这不是我的专长
/**
* Parse a string according to CSV format (https://tools.ietf.org/html/rfc4180), with variabele delimiter (default ,).
* @param string $string String to be parsed as csv
* @param string $delimiter character to be used as field delimiter
* @return array Array with for each line an array with csv field values
*/
function csv_parse ($string, $delimiter = ",", $line_mode = true) {
// This function parses on line-level first ($line_mode = true) and calls itself recursively to parse each line on field-level ($line_mode = false).
// when in line mode, the delimiter is eol (\n, \r\n and \n\r).
// when in field mode, the delimiter is the passed $delimiter.
$delimiter = substr ($delimiter,0,1); // delimiter is one character
$length = strlen ($string);
$parsed_array = array();
$end_of_line_state = false;
$enclosed_state = false;
$i = 0;
$field = "";
do {
switch (true) {
case (!$enclosed_state && $end_of_line_state && ($string[$i] == "\r" && $string[$i-1] == "\n")) :
case (!$enclosed_state && $end_of_line_state && ($string[$i] == "\n" && $string[$i-1] == "\r")) :
// ...found second character of eol (\r\n of \n\r). Ignore
$end_of_line_state = false;
break;
case (!$enclosed_state && !$end_of_line_state && ($string[$i] == "\n" || $string[$i] == "\r")) :
// ... found first character of eol \n, \r\n of \n\r
$end_of_line_state = true; // eol can be two characters, so prepare for the second
if ($field != "") { // ignore empty lines. Prohibited in csv
$parsed_array [] = csv_parse ($field, $delimiter, false); // recursive call to parse on field-level. Flush result
};
$field = ""; // prepare for next one
break;
case (!$enclosed_state && $string[$i] == $delimiter && !$line_mode) :
// ...delimiter found
$parsed_array [] = $field; // flush field as new array element
$field = ""; // prepare for next one
break;
case ($string[$i] == "\"") :
// ...encloser found
if ($enclosed_state) {
if ($i < $length && $string[$i+1] == "\"") {
// ... escaped " found
if (!$line_mode) {
$field .= "\""; // when parsing fieldlevel, only " is part of the line
} else {
$field .= "\"\""; // when parsing line level, the escaping " is also part of the line
};
$i++;
} else {
// ...closing encloser found
$enclosed_state = false;
if ($line_mode) {
$field .= $string[$i]; // when parsing line level, the enclosing " are part of the line
};
};
} else {
// ... opening encloser found
$enclosed_state = true;
if ($line_mode) {
$field .= $string[$i]; // when parsing line level, the enclosing " are part of the line
};
};
break;
default:
// ...regular character found
$field .= $string[$i];
};
$i++;
if ($i >= $length) { // end of string
if ($line_mode) {
$parsed_array [] = csv_parse ($field, $delimiter, false); // recursive call to parse on field-level. Flush result.
} else {
$parsed_array [] = $field; // flush last field
};
};
} while ($i < $length);
return $parsed_array;
};
/**
*根据CSV格式解析字符串(https://tools.ietf.org/html/rfc4180),带有可变分隔符(默认值)。
*@param string$string要解析为csv
*@param string$分隔符字符用作字段分隔符
*@return array,每行包含一个带有csv字段值的数组
*/
函数csv_parse($string,$delimiter=“,”,$line_mode=true){
//此函数首先在行级别上解析($line_mode=true),然后递归调用自身以解析字段级别上的每一行($line_mode=false)。
//在行模式下,分隔符为eol(\n、\r\n和\n\r)。
//在字段模式下,分隔符是传递的$delimiter。
$delimiter=substr($delimiter,0,1);//分隔符是一个字符
$length=strlen($string);
$parsed_array=array();
$end_of_line_state=false;
$state=false;
$i=0;
$field=“”;
做{
开关(真){
大小写(!$included_state&&$end_of_line_state&&($string[$i]=“\r”&&&$string[$i-1]=“\n”):
大小写(!$included_state&&$end_of_line_state&&($string[$i]=“\n”&&&$string[$i-1]=“\r”):
//…找到eol的第二个字符(\r\n of \n\r)。忽略
$end_of_line_state=false;
打破
大小写(!$consedded_state&&!$end_of_line_state&&($string[$i]=“\n”| |$string[$i]=“\r”):
//…找到下线的第一个字符\n\r\n的\n\r\n
$end\u of_line\u state=true;//eol可以是两个字符,所以请准备第二个字符
如果($field!=“”){//忽略空行。csv中禁止
$parsed_array[]=csv_parse($field,$delimiter,false);//递归调用在字段级别进行解析。刷新结果
};
$field=”“;//准备下一个
打破
大小写(!$enclosed_state&&$string[$i]==$delimiter&&!$line_模式):
//…找到分隔符
$parsed_数组[]=$field;//将字段作为新数组元素刷新
$field=”“;//准备下一个
打破
大小写($string[$i]==“\”):
//…找到封闭器
如果($U随附州){
如果($i<$length&&$string[$i+1]==“\”){
//“逃走”被发现
如果(!$line\u模式){
$field.=“\”“;//分析fieldlevel时,only”是行的一部分
}否则{
$field.=“\”\”;//在分析行级别时,转义“也是行的一部分
};
$i++;
}否则{
//…找到封闭程序
$state=false;
如果($line\u模式){
$field.=$string[$i];//分析行级别时,封闭的“字段”是行的一部分
};
};
}否则{
//…找到打开的封闭器
$state=true;
如果($line\u模式){
$field.=$string[$i];//分析行级别时,封闭的“字段”是行的一部分
};
};
打破
违约:
//…找到正则字符
$field.=$string[$i];
};
$i++;
如果($i>=$length){//字符串的结尾
如果($line\u模式){
$parsed_array[]=csv_parse($field,$delimiter,false);//递归调用在字段级别进行解析。刷新结果。
}否则{
$parsed_数组[]=$field;//刷新最后一个字段
};
};
}而($i<$length);
返回$parsed_数组;
};
Hm.奇怪。我在这儿闲逛了一会儿
$a = str_getcsv("\"@ne piece, @f text\"", "\n");
$b = str_getcsv("\"@ne piece, @f text\"", "\n","\"");
$c = str_getcsv("\"@ne piece, @f text\"", "\n","@");
echo $a; // @ne piece, @f text
echo $b; // @ne piece, @f text
echo $c; // "@ne piece, @f text"