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字符串(上传文件的内容):

  • 将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"