Charlestone
Charlestone

Reputation: 1260

str_getcsv() not ignoring quoted new lines

i need to parse a CSV string and in a first step, I would like to get lines from it. I use str_getcsv function to do it, but it seems to fail even in the most basic scenario - new line surrounded by quotes.

$rows = '7;"Hi
";3';
$array = str_getcsv($rows,"\n",'"');
print_r($array);

The result should be array with just one value, but I got two - surprisingly where the quoted new line is...

result:
Array
(
    [0] => 7;"Hi
    [1] => ;3
)

What am I doing wrong?

EDIT: Weird, but when I tried

$rows = '7;"Hi
";3
8;Hello;6';

The result is

Array
(
    [0] => 7;"Hi
    [1] => ;3
8;Hello;6
)

Upvotes: 6

Views: 4808

Answers (5)

Prescol
Prescol

Reputation: 627

Use fgetcsv instead. This method handles newlines enclosed with quotes. You can also work with a string creating a resource from it.


        $stream = fopen('data://text/plain,' . $csv, 'r');
        $rows = [];
        while (($row = fgetcsv($stream)) !== false) {
            $rows[] = $row;
        }
        
        var_dump($rows)

Upvotes: 0

Charlestone
Charlestone

Reputation: 1260

It seems like str_getcsv() does not allow to have newlines in string fields and parses the lines by newline without checking, whether the new line is in field or not. It is necessary to use something different than this function, it just does not work that well.

I found code which directly parses CSV without a need to parse it into lines at man page of the str_getcsv() function from user normadize -a- gmail -d- com. All whats missing is that it does not use values from the first line as keys for other lines.

function parse_csv ($csv_string, $delimiter = ",", $skip_empty_lines = true, $trim_fields = true)
{
    $enc = preg_replace('/(?<!")""/', '!!Q!!', $csv_string);
    $enc = preg_replace_callback(
        '/"(.*?)"/s',
        function ($field) {
            return urlencode(utf8_encode($field[1]));
        },
        $enc
    );
    $lines = preg_split($skip_empty_lines ? ($trim_fields ? '/( *\R)+/s' : '/\R+/s') : '/\R/s', $enc);
    return array_map(
        function ($line) use ($delimiter, $trim_fields) {
            $fields = $trim_fields ? array_map('trim', explode($delimiter, $line)) : explode($delimiter, $line);
            return array_map(
                function ($field) {
                    return str_replace('!!Q!!', '"', utf8_decode(urldecode($field)));
                },
                $fields
            );
        },
        $lines
    );
}

?>

Upvotes: 1

C3roe
C3roe

Reputation: 96306

What am I doing wrong?

You called the function with the wrong parameters ...

http://php.net/manual/en/function.str-getcsv.php:

delimiter
Set the field delimiter (one character only).

The second parameter is the field delimiter, and that is ;, not the newline.

$rows = '7;"Hi
";3';
$array = str_getcsv($rows,';','"');
var_dump($array);

// output:
array(3) {
  [0]=>
  string(1) "7"
  [1]=>
  string(4) "Hi
"
  [2]=>
  string(1) "3"
}

EDIT:

The result should be array with just one value

Why one value? What is your actual field delimiter character supposed to be here? I assumed it was the ; ... but if you expect to get only one value from the shown example input, then what is the delimiter? Or how is it even CSV …?

2nd Edit:

first I need to get lines - field delimiter is a newline

That is not really how str_getcsv works - that assumes that you have just one single line of your CSV data as your input string already. And you can’t really use something like a simple explode at newline to “get” single lines - because the newline character can also be inside the fields.

fgetcsv would be the proper function to achieve both ... but that operates on files, not string data. Perhaps PHP’s various streams could help with that somehow, if you “write” your string data into a temp file or memory, so that you could actually use fopen to get a readable stream that fgetcsv can then work with ... this question can give you an idea of how that could work: how to use fgetcsv with strings

Upvotes: 0

Oleg
Oleg

Reputation: 109

Working on

php -v PHP 5.6.30 (cli) (built: Feb 7 2017 16:18:37)

php > $row = "\"abc\"\n;\"def\"\n;\"123\"";
php > $array = str_getcsv($row,"\n",'"');
php > print_r($array);
Array
(
    [0] => abc
    [1] => ;"def"
    [2] => ;"123"
)

and this

php > $row = "\"abc\"\n\"def\"\n\"123\"";
php > 
php > 
php > $array = str_getcsv($row,"\n",'"');
php > print_r($array);
Array
(
    [0] => abc
    [1] => def
    [2] => 123
)

etc (all other cases).

May be have you another symbols in delimiters in your var statement here distinguishing from \n or \r or \r\n?

Upvotes: 0

apokryfos
apokryfos

Reputation: 40663

PHP doesn't seem to allow newlines within column data. You can probably work around this though:

$row = '7;"Hi'.PHP_EOL.'";3'; 
$row=str_replace(PHP_EOL,"\0",$row); 
$array = str_getcsv($row,"\n",'"');
$array = array_map(function ($v) {
   return str_replace("\0",PHP_EOL,$v);
},$array);
print_r($array);

Array
(
    [0] => 7;"Hi
";3
)

Upvotes: 0

Related Questions