2016-04-14 3 views
2

Ich habe festgestellt, dass nicht den ersten Wert in doppelte Anführungszeichen eingeschlossen, auch wenn die Zeichenfolge Daten auf diese Weise übergeben wird.str_getcsv umschließt die erste Spalte in doppelten Anführungszeichen nicht im CSV-Format

In dem folgenden Beispiel ist der erste Wert in der dritten Zeile "Small Box, But Smaller", aber nachdem er durchlaufen hat, wird er Small Box, But Smaller (ohne doppelte Anführungszeichen). Wie folgt aus:

// multi-line csv string 
$csvString = <<<'CSV' 
"Title","Description",Quantity 
"Small Box","For storing magic beans.",2 
"Small Box, But Smaller","Not sure why we need this.",0 
CSV; 

// split string into rows (don't use explode in case multi-line values exist) 
$csvRows = str_getcsv($csvString, "\n"); // parse rows 
echo '<pre>'; 
print_r($csvRows); 
echo '</pre>'; 

Ausgänge:

Array 
(
    [0] => Title,"Description",Quantity 
    [1] => Small Box,"For storing magic beans.",2 
    [2] => Small Box, But Smaller,"Not sure why we need this.",0 
) 

Das Problem verursacht dies ist, dass jetzt, wenn jede Zeile analysiert wird mit str_getcsv, ein Komma in dem ersten Wert macht es in zwei Reihen aufgeteilt. Wenn es hält dies ausgeführt wird:

foreach($csvRows as &$csvRow) { 
    $csvRow = str_getcsv($csvRow); // parse each row into values and save over original array value 
} 
unset($csvRow); // clean up 

// output 
echo '<pre>'; 
print_r($csvRows); 
echo '</pre>'; 

Ausgänge:

Array 
(
    [0] => Array 
     (
      [0] => Title 
      [1] => Description 
      [2] => Quantity 
     ) 

    [1] => Array 
     (
      [0] => Small Box 
      [1] => For storing magic beans. 
      [2] => 2 
     ) 

    [2] => Array 
     (
      [0] => Small Box 
      [1] => But Smaller 
      [2] => Not sure why we need this. 
      [3] => 0 
     ) 

) 

Das Problem ist in dem letzten Array-Wert, der ein Array von 4 Tasten statt 3. Es ist auf dem Komma des Wertes geteilt "Small Box, But Smaller".

Auf der anderen Seite, das Parsen nur eine Zeile String funktioniert:

$csvRowData = '"Small Box, But Smaller","Not sure why we need this.",0'; 
$csvValues = str_getcsv($csvRowData); 

echo '<pre>'; 
print_r($csvValues); 
echo '</pre>'; 

Ausgänge:

Array 
(
    [0] => Small Box, But Smaller 
    [1] => Not sure why we need this. 
    [2] => 0 
) 

Warum ist das passiert und wie löse ich das Problem mit mehrzeiligen CSV-Daten? Gibt es eine bewährte Methode zum Arbeiten mit CSV-Daten mit mehreren Zeilen, wenn es sich um eine Zeichenfolge handelt und nicht direkt aus einer Datei gelesen wird? Außerdem muss ich mehrzeilige Werte behandeln, wie zum Beispiel "foo \n bar", so dass ich nicht einfach explode() statt der ersten str_getcsv() verwenden kann.

+0

Wenn ich 'explode (PHP_EOL, $ csvString)' verwenden würde, würde es auf mehrzeilige Werte aufgeteilt werden. Für meinen Server ist PHP_EOL '\ n' und das Newline-Zeichen in meiner CSV-Datei ist auch' \ n'. – Anton

+0

In diesem Fall sollten Sie wahrscheinlich darauf hinweisen, dass einige Ihrer CSV-Werte Zeilenumbrüche enthalten können. – Phil

+0

Warum sollte ich 'str_getcsv' nicht verwenden, um in Zeilen zu teilen? Gibt es eine Alternative? Und 'explode()' wird nicht funktionieren. Zum Beispiel, wenn ich diese Zeile habe: '" Hallo Welt "," foo \ n bar "', mit 'explode()' würde es in zwei Zeilen, rechts unten den Wert von '" foo \ n bar "' – Anton

Antwort

2

Nach vielen Kopfschmerzen glaube ich das Problem jetzt zu verstehen. Laut den PHP-Leuten ist "str_getcsv() dafür ausgelegt, einen einzelnen CSV-Datensatz in Felder zu zerlegen" (siehe https://bugs.php.net/bug.php?id=55763). Ich entdeckte, dass str_getcsv() für mehrere Zeilen verursacht diese nicht so gut dokumentiert Probleme mit:

  • Doppelte Anführungszeichen nicht gehalten werden (wie ich demontrate oben).
  • Zeilenunterbrechungen in Werten bewirken, dass eine neue Zeile begonnen hat. Dies kann viele unbeabsichtigte Folgen haben.

Ich löste das Problem, indem ich eine temporäre Datei erstellte und den CSV-Inhalt darauf schrieb. Dann lese ich die Datei mit fgetcsv(), die nicht zu den 2 oben beschriebenen Problemen führte. Beispielcode:

// multi-line csv string 
$csvString = <<<'CSV' 
"Title","Description",Quantity 
"Small Box","For storing magic beans.",2 
"Small Box, But Smaller","This value 
contains 
multiple 
lines.",0 
CSV; 
//^notice the multiple lines in the last row's value 

// create a temporary file 
$tempFile = tmpfile(); 
// write the CSV to the file 
fwrite($tempFile, $csvString); 
// go to first character 
fseek($tempFile, 0); 

// track CSV rows 
$csvRows = array(); 
// read the CSV temp file line by line 
while (($csvColumns = fgetcsv($tempFile)) !== false) { 
    $csvRows[] = $csvColumns; // push columns to array (really it would be more memory-efficient to process the data here and not append to an array) 
} 

// Close and delete the temp file 
fclose($tempFile); 

// output 
echo '<pre>'; 
print_r($csvRows); 
echo '</pre>'; 

Ergebnisse in:

Array 
(
    [0] => Array 
     (
      [0] => Title 
      [1] => Description 
      [2] => Quantity 
     ) 

    [1] => Array 
     (
      [0] => Small Box 
      [1] => For storing magic beans. 
      [2] => 2 
     ) 

    [2] => Array 
     (
      [0] => Small Box, But Smaller 
      [1] => This value 
contains 
multiple 
lines. 
      [2] => 0 
     ) 

) 

Ich werde auch hinzufügen, dass ich einige Optionen auf GitHub und 2 große Projekte für PHP 5.4+ und PHP 5.5+ gefunden. Ich verwende jedoch immer noch PHP 5.3 und habe nur Optionen mit eingeschränkter Aktivität gesehen. Des Weiteren können einige dieser CSV-Strings auch in Dateien geschrieben und ausgelesen werden.

Ich sollte auch beachten, dass die Dokumentation für PHP einige Kommentare über str_getcsv() nicht RFC-konform ist: http://php.net/manual/en/function.str-getcsv.php. Dasselbe scheint für fgetcsv() zu gelten, doch letzteres hat zumindest in diesem Fall meine Bedürfnisse erfüllt.

0

Ich weiß nicht, warum Sie PHP_EOL nicht richtig funktioniert, wie es auf meinem Server tut, aber ich habe dieses Problem vor.

Der Ansatz, den ich nahm, geht wie folgt.

Zunächst möchte ich sicherstellen, dass alle meine Felder durch doppelte Anführungszeichen, unabhängig vom Wert im Bereich umgeben sind, so dass Ihr Beispiel Text zu verwenden (mit einigen geringfügigen Änderungen):

// multi-line csv string 
$csvString = <<<CSV 
"Title","Description","Quantity" 
"Small Box","For storing magic beans.","2" 
"Small Box, But Smaller","Not sure why we need this.","0" 
"a","\n","b","c" 

CSV; 

$csvString .= '"a","' . "\n" . '","' . PHP_EOL . '","c"'; 

Zweitens zielen ich solo PHP_EOL das kann Verweilen in Werten sein, damit ich mit keinen „PHP_EOL“ Strings ersetzen kann „\ r \ n“

// Clear any solo end of line characters that are within values 
$csvString = str_replace('","' . PHP_EOL . '"', '",""',$csvString); 
$csvString = str_replace('"' . PHP_EOL . '","', '"","',$csvString); 

$csvString = str_replace('"' . PHP_EOL . '"', '"'. "\r\n" . '"',$csvString); 

und dann schließlich das ermöglicht es mir, die Verwendung von pHP-Funktion und die Display-Ausgabe explodieren:

$csvArr = explode("\r\n",$csvString); 
foreach($csvArr as &$csvRow) { 
    $csvRow = str_getcsv($csvRow); // parse each row into values and save over original array value 
} 
unset($csvRow); // clean up 

// output 
echo '<pre>'; 
print_r($csvArr); 
echo '</pre>'; 

Welche Ausgänge:

Array 
(
    [0] => Array 
     (
      [0] => Title 
      [1] => Description 
      [2] => Quantity 
     ) 

    [1] => Array 
     (
      [0] => Small Box 
      [1] => For storing magic beans. 
      [2] => 2 
     ) 

    [2] => Array 
     (
      [0] => Small Box, But Smaller 
      [1] => Not sure why we need this. 
      [2] => 0 
     ) 

    [3] => Array 
     (
      [0] => a 
      [1] => 

      [2] => b 
      [3] => c 
     ) 

    [4] => Array 
     (
      [0] => a 
      [1] => 

      [2] => 
      [3] => c 
     ) 

) 

Wie Sie aus der Ausgabe der neuen Linie Zeichen ausgerichtet sind, nicht sehen können, nur die PHP_EOL.

Verwandte Themen