PHP Doku:: Parst einen CSV-String in ein Array - function.str-getcsv.html

Verlauf / Chronik / History: (38) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchFunktionsreferenzTextverarbeitungZeichenkettenString-Funktionenstr_getcsv

Ein Service von Reinhard Neidl - Webprogrammierung.

String-Funktionen

<<sscanf

str_ireplace>>

str_getcsv

(PHP 5 >= 5.3.0)

str_getcsv Parst einen CSV-String in ein Array

Beschreibung

array str_getcsv ( string $input [, string $delimiter = ',' [, string $enclosure = '"' [, string $escape = '\\' ]]] )

Wie die Funktion fgetcsv() parst diese Funktion einen String als Eingabe. Dieser muss jedoch direkt übergeben werden, im Gegensatz zu fgetcsv(), das eine Datei als Eingabe erwartet.

Parameter-Liste

input

Die zu parsende Zeichenkette.

delimiter

Bestimmt das Feldtrennzeichen (nur ein Zeichen).

enclosure

Bestimmt das Textmarkierungszeichen (nur ein Zeichen).

escape

Bestimmt das Maskierungszeichen (nur ein Zeichen). Standardmäßig wird ein Backslash (\) verwendet.

Rückgabewerte

Gibt ein indexiertes Array zurück, das die eingelesenen Felder enthält.

Siehe auch

  • fgetcsv() - Liest eine Zeile von der Position des Dateizeigers und prüft diese auf Komma-Separierte-Werte (CSV)


20 BenutzerBeiträge:
- Beiträge aktualisieren...
Anonymous
25.10.2010 9:25
If your version of PHP doesn't have `str_getcsv` and you don't need custom $escape or $eol values, try this:

<?php if (!function_exists('str_getcsv')) {
 
function
str_getcsv($input, $delimiter=',', $enclosure='"', $escape=null, $eol=null) {
 
$temp=fopen("php://memory", "rw");
 
fwrite($temp, $input);
 
fseek($temp, 0);
 
$r=fgetcsv($temp, 4096, $delimiter, $enclosure);
 
fclose($temp);
  return
$r;
}
 
}
?>
Jay Williams
10.08.2010 22:50
Here is a quick and easy way to convert a CSV file to an associated array:

<?php
/**
 * @link http://gist.github.com/385876
 */
function csv_to_array($filename='', $delimiter=',')
{
    if(!
file_exists($filename) || !is_readable($filename))
        return
FALSE;

   
$header = NULL;
   
$data = array();
    if ((
$handle = fopen($filename, 'r')) !== FALSE)
    {
        while ((
$row = fgetcsv($handle, 1000, $delimiter)) !== FALSE)
        {
            if(!
$header)
               
$header = $row;
            else
               
$data[] = array_combine($header, $row);
        }
       
fclose($handle);
    }
    return
$data;
}

?>
hpartidas at deuz dot net
25.05.2010 18:50
I found myself wanting to parse a CSV and didn't have access to str_getcsv, so I wrote substitute for PHP < 5.3, hope it helps someone out there stuck in the same situation.

<?php
if (!function_exists('str_getcsv')) {
    function
str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
        if (
is_string($input) && !empty($input)) {
           
$output = array();
           
$tmp    = preg_split("/".$eol."/",$input);
            if (
is_array($tmp) && !empty($tmp)) {
                while (list(
$line_num, $line) = each($tmp)) {
                    if (
preg_match("/".$escape.$enclosure."/",$line)) {
                        while (
$strlen = strlen($line)) {
                           
$pos_delimiter       = strpos($line,$delimiter);
                           
$pos_enclosure_start = strpos($line,$enclosure);
                            if (
                               
is_int($pos_delimiter) && is_int($pos_enclosure_start)
                                && (
$pos_enclosure_start < $pos_delimiter)
                                ) {
                               
$enclosed_str = substr($line,1);
                               
$pos_enclosure_end = strpos($enclosed_str,$enclosure);
                               
$enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
                               
$output[$line_num][] = $enclosed_str;
                               
$offset = $pos_enclosure_end+3;
                            } else {
                                if (empty(
$pos_delimiter) && empty($pos_enclosure_start)) {
                                   
$output[$line_num][] = substr($line,0);
                                   
$offset = strlen($line);
                                } else {
                                   
$output[$line_num][] = substr($line,0,$pos_delimiter);
                                   
$offset = (
                                                !empty(
$pos_enclosure_start)
                                                && (
$pos_enclosure_start < $pos_delimiter)
                                                )
                                                ?
$pos_enclosure_start
                                               
:$pos_delimiter+1;
                                }
                            }
                           
$line = substr($line,$offset);
                        }
                    } else {
                       
$line = preg_split("/".$delimiter."/",$line);
   
                       
/*
                         * Validating against pesky extra line breaks creating false rows.
                         */
                       
if (is_array($line) && !empty($line[0])) {
                           
$output[$line_num] = $line;
                        } 
                    }
                }
                return
$output;
            } else {
                return
false;
            }
        } else {
            return
false;
        }
    }
}
?>
vincent dot enjalbert at gmail dot com
19.02.2010 12:37
For multiline array, just use :

<?php
$csvData
= file_get_contents($fileName);
$csvNumColumns = 22;
$csvDelim = ";";
$data = array_chunk(str_getcsv($csvData, $csvDelim), $csvNumColumns);
?>
Raymond
15.12.2009 2:49
Here's a little function to convert a multi-line CSV string to an array:

<?php
function csv_to_array($csv, $delimiter = ',', $enclosure = '"', $escape = '\\', $terminator = "\n") {
   
$r = array();
   
$rows = explode($terminator,trim($csv));
   
$names = array_shift($rows);
   
$names = str_getcsv($names,$delimiter,$enclosure,$escape);
   
$nc = count($names);
    foreach (
$rows as $row) {
        if (
trim($row)) {
           
$values = str_getcsv($row,$delimiter,$enclosure,$escape);
            if (!
$values) $values = array_fill(0,$nc,null);
           
$r[] = array_combine($names,$values);
        }
    }
    return
$r;
}
?>
Jrg Wagner
12.10.2009 23:23
Here is a very concise replacement for str_getcsv.
No escaping of the enclosure char though, but an additional possibility to preserve the enclosing characters around a field.
Note that the fourth parameter therefore has a different meaning!

<?php
function csv_explode($delim=',', $str, $enclose='"', $preserve=false){
 
$resArr = array();
 
$n = 0;
 
$expEncArr = explode($enclose, $str);
  foreach(
$expEncArr as $EncItem){
    if(
$n++%2){
     
array_push($resArr, array_pop($resArr) . ($preserve?$enclose:'') . $EncItem.($preserve?$enclose:''));
    }else{
     
$expDelArr = explode($delim, $EncItem);
     
array_push($resArr, array_pop($resArr) . array_shift($expDelArr));
     
$resArr = array_merge($resArr, $expDelArr);
    }
  }
  return
$resArr;
}
?>
dave_walter at yahoo dot com
4.06.2009 18:51
Just to clarify, my str_putcsv() function was only ever designed to complement the functionality of the str_getcsv() built-in function, which can only handle converting one line of input into a single level array. For example, this code:

<?php
    var_dump
( str_getcsv( "a,b,c\nd,e,f", "," ));
?>

generates this output:

array(5) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    string(3) "c
    d"
    [3]=>
    string(1) "e"
    [4]=>
    string(1) "f"
}

Even fgetcsv() and fputcsv() only work with a single line. All the examples show them being used within a loop of some sort.

I was also avoiding the artificial restriction on the length of the CSV string introduced by Ulf's modification.
Ulf
28.05.2009 15:24
As Dave's function also had the problem with only one line being returned here's a slightly changed version:

<?php
function str_putcsv($input, $delimiter = ',', $enclosure = '"') {
 
// Open a memory "file" for read/write...
 
$fp = fopen('php://temp', 'r+');
 
// ... write the $input array to the "file" using fputcsv()...
 
fputcsv($fp, $input, $delimiter, $enclosure);
 
// ... rewind the "file" so we can read what we just wrote...
 
rewind($fp);
 
// ... read the entire line into a variable...
 
$data = fread($fp, 1048576); // [changed]
  // ... close the "file"...
 
fclose($fp);
 
// ... and return the $data to the caller, with the trailing newline from fgets() removed.
 
return rtrim( $data, "\n" );
}
?>
It assumes that one line won't exceed 1Mb of data. That should be more than enough.
Anonymous
17.03.2009 7:02
For some reason o'connor's code only reads one line of a csv for me... I had to replace the line

      $data = fgetcsv($fp, 1000, $delimiter, $enclosure); //  $escape only got added in 5.3.0

with this:

      $data;
      while (!feof($fp))
      {
        $data[] = fgetcsv($fp, 0, $delimiter, $enclosure); //  $escape only got added in 5.3.0
      }

...to get all of the data out of my string (some post data pasted into a textbox and processed only with stripslashes).
dave_walter at NOSPAM dot yahoo dot com
7.02.2009 0:32
Drawing inspiration from daniel dot oconnor at gmail dot com, here's an alternative str_putcsv() that leverages existing PHP core functionality (5.1.0+) to avoid re-inventing the wheel.

<?php
if(!function_exists('str_putcsv')) {
    function
str_putcsv($input, $delimiter = ',', $enclosure = '"') {
       
// Open a memory "file" for read/write...
       
$fp = fopen('php://temp', 'r+');
       
// ... write the $input array to the "file" using fputcsv()...
       
fputcsv($fp, $input, $delimiter, $enclosure);
       
// ... rewind the "file" so we can read what we just wrote...
       
rewind($fp);
       
// ... read the entire line into a variable...
       
$data = fgets($fp);
       
// ... close the "file"...
       
fclose($fp);
       
// ... and return the $data to the caller, with the trailing newline from fgets() removed.
       
return rtrim( $data, "\n" );
    }
}
?>
Jeremy
21.01.2009 2:20
After using several methods in the past to create CSV strings without using files (disk IO sucks), I finally decided it's time to write a function to handle it all. This function could use some cleanup, and the variable type test might be overkill for what is needed, I haven't thought about it too much.

Also, I took the liberty of replacing fields with certain data types with strings which I find much easier to work with. Some of you may not agree with those. Also, please note that the type "double" or float has been coded specifically for two digit precision because if I am using a float, it's most likely for currency.

I am sure some of you out there would appreciate this function.

<?php
   
function str_putcsv($array, $delimiter = ',', $enclosure = '"', $terminator = "\n") {
       
# First convert associative array to numeric indexed array
       
foreach ($array as $key => $value) $workArray[] = $value;

       
$returnString = '';                 # Initialize return string
       
$arraySize = count($workArray);     # Get size of array
       
       
for ($i=0; $i<$arraySize; $i++) {
           
# Nested array, process nest item
           
if (is_array($workArray[$i])) {
               
$returnString .= str_putcsv($workArray[$i], $delimiter, $enclosure, $terminator);
            } else {
                switch (
gettype($workArray[$i])) {
                   
# Manually set some strings
                   
case "NULL":     $_spFormat = ''; break;
                    case
"boolean"$_spFormat = ($workArray[$i] == true) ? 'true': 'false'; break;
                   
# Make sure sprintf has a good datatype to work with
                   
case "integer"$_spFormat = '%i'; break;
                    case
"double":   $_spFormat = '%0.2f'; break;
                    case
"string":   $_spFormat = '%s'; break;
                   
# Unknown or invalid items for a csv - note: the datatype of array is already handled above, assuming the data is nested
                   
case "object":
                    case
"resource":
                    default:        
$_spFormat = ''; break;
                }
                               
$returnString .= sprintf('%2$s'.$_spFormat.'%2$s', $workArray[$i], $enclosure);
$returnString .= ($i < ($arraySize-1)) ? $delimiter : $terminator;
            }
        }
       
# Done the workload, return the output information
       
return $returnString;
    }

?>
daniel dot oconnor at gmail dot com
20.01.2009 1:48
Don't have this? Ask fgetcsv() to do it for you.

5.1.0+

<?php
if (!function_exists('str_getcsv')) {
    function
str_getcsv($input, $delimiter = ",", $enclosure = '"', $escape = "\\") {
       
$fiveMBs = 5 * 1024 * 1024;
       
$fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+');
       
fputs($fp, $input);
       
rewind($fp);

       
$data = fgetcsv($fp, 1000, $delimiter, $enclosure); //  $escape only got added in 5.3.0

       
fclose($fp);
        return
$data;
    }
}
?>
Anonymous
12.01.2009 17:55
Thanks Rob, have used your code. It needs a minor tweak so that the delimiter option is processed as it should be. Line 5 should read:

<?php    $expr="/$delimiter(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/"; // added ?>

instead of:

<?php    $expr="/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/"; // added ?>

JS, London
Rob
7.11.2008 17:54
This does the same as the example below, giving you an array, but it allows to parse csv with commas in between quotes (used for addresses)... and it takes out the quotes from an array entry as well.

<?php
function parse_csv($file, $options = null) {
   
$delimiter = empty($options['delimiter']) ? "," : $options['delimiter'];
   
$to_object = empty($options['to_object']) ? false : true;
   
$expr="/$delimiter(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/"; // added
   
$str = $file;
   
$lines = explode("\n", $str);
   
$field_names = explode($delimiter, array_shift($lines));
    foreach (
$lines as $line) {
       
// Skip the empty line
       
if (empty($line)) continue;
       
$fields = preg_split($expr,trim($line)); // added
       
$fields = preg_replace("/^\"(.*)\"$/s","$1",$fields); //added
        //$fields = explode($delimiter, $line);
       
$_res = $to_object ? new stdClass : array();
        foreach (
$field_names as $key => $f) {
            if (
$to_object) {
               
$_res->{$f} = $fields[$key];
            } else {
               
$_res[$f] = $fields[$key];
            }
        }
       
$res[] = $_res;
    }
    return
$res;
}
?>

[EDIT BY danbrown AT php DOT net: Includes bugfixes provided by "Anonymous" on 12-JAN-09 and "joelfromlollypins.com" on 19-JAN-09.]
william dot j dot weir at gmail dot com
18.09.2008 12:19
If your happy enough having just a multi-dimensional array, this should work fine. I had wanted to use the one provided by keananda but it was choking on pr($lines).

<?php
function f_parse_csv($file, $longest, $delimiter) {
 
$mdarray = array();
 
$file    = fopen($file, "r");
  while (
$line = fgetcsv($file, $longest, $delimiter)) {
   
array_push($mdarray, $line);
    }
 
fclose($file);
  return
$mdarray;
  }
?>

$longest is a number that represents the longest line in the csv file as required by fgetcsv().  The page for fgetcsv() said that the longest line could be set to 0 or left out, but I couldn't get it to work without. I just made it extra large when I had to use it.
keananda at gmail dot com
15.09.2008 13:29
For those who need this function but not yet installed in their environment, you can use my function bellow.

You can parse your csv file into an associative array (by default) for each lines, or into an object.
<?php
function parse_csv($file, $options = null) {
   
$delimiter = empty($options['delimiter']) ? "," : $options['delimiter'];
   
$to_object = empty($options['to_object']) ? false : true;
   
$str = file_get_contents($file);
   
$lines = explode("\n", $str);
   
pr($lines);
   
$field_names = explode($delimiter, array_shift($lines));
    foreach (
$lines as $line) {
       
// Skip the empty line
       
if (empty($line)) continue;
       
$fields = explode($delimiter, $line);
       
$_res = $to_object ? new stdClass : array();
        foreach (
$field_names as $key => $f) {
            if (
$to_object) {
               
$_res->{$f} = $fields[$key];
            } else {
               
$_res[$f] = $fields[$key];
            }
        }
       
$res[] = $_res;
    }
    return
$res;
}
?>

NOTE:
Line number 1 of the csv file will be considered as header (field names).

TODO:
- Enclosure handling
- Escape character handling
- Other features/enhancements as you need

EXAMPLE USE:
Content of /path/to/file.csv:
CODE,COUNTRY
AD,Andorra
AE,United Arab Emirates
AF,Afghanistan
AG,Antigua and Barbuda

<?php
$arr_csv
= parse_csv("/path/to/file.csv");
print_r($arr_csv);
?>
// Output:
Array
(
    [0] => Array
        (
            [CODE] => AD
            [COUNTRY] => Andorra
        )
    [1] => Array
        (
            [CODE] => AE
            [COUNTRY] => United Arab Emirates
        )
    [2] => Array
        (
            [CODE] => AF
            [COUNTRY] => Afghanistan
        )
    [3] => Array
        (
            [CODE] => AG
            [COUNTRY] => Antigua and Barbuda
        )
)

<?php
$obj_csv
= parse_csv("/path/to/file.csv", array("to_object" => true));
print_r($obj_csv);
?>
// Output:
Array
(
    [0] => stdClass Object
        (
            [CODE] => AD
            [COUNTRY] => Andorra    
        )
    [1] => stdClass Object
        (
            [CODE] => AE
            [COUNTRY] => United Arab Emirates    
        )
    [2] => stdClass Object
        (
            [CODE] => AF
            [COUNTRY] => Afghanistan    
        )
    [3] => stdClass Object
        (
            [CODE] => AG
            [COUNTRY] => Antigua and Barbuda    
        )
    [4] => stdClass Object
        (
            [CODE] =>
            [COUNTRY] =>
        )
)

// If you use character | (pipe) as delimiter in your csv file, use:
<?php
$arr_csv
= parse_csv("/path/to/file.csv", array("delimiter"=>"|"));
?>

==NSD==
colin_mckinnon at slc dot co dot uk
9.06.2008 18:00
Regarding RFC 4180 - I asked the author about the specifics of the format described which poses a number of technical difficulties in building a parser - his response is shown below:

quote:

Please do keep one thing in mind - this RFC
was meant to define the MIME type for CSV rather than the format. It
happens to be that no format definition existed and I was forced to
define one. As the RFC states:

"This section documents the format that seems to be followed by most
implementations"
csv at rfc dot org
5.05.2008 22:15
RFC 4180 which deals with CSVs states the escape character is supposed to be a double quotation mark: (page 2)
   7.  If double-quotes are used to enclose fields, then a double-quote
       appearing inside a field must be escaped by preceding it with
       another double quote.  For example:

       "aaa","b""bb","ccc"
peev[dot]alexander at gmail dot com
20.04.2008 23:22
CSV parsing and storage is not that hard to implement - see my example functions ( I believe they do a pretty good job - I use them in a production environment ):

<?php

if( !function_exists("parse_csv") ){
    function
parse_csv($string){
/* Author : Alexander Peev, posted at PHP.NET */
       
if( !function_exists("parse_csv_aux") ){
            function
parse_csv_aux( $string ){
               
$product = "";
               
$in_quote = FALSE;
               
$skipped_quote = FALSE;
                for( 
$i = 0 ; $i < strlen($string) ; $i++  ){
                    if(
$string{$i} == "\"" ){
                        if(
$in_quote){
                            if(
$skipped_quote){
                               
$product .= "\"";
                               
$skipped_quote = FALSE;
                            }
                            else if( !
$skipped_quote ){
                               
$skipped_quote = TRUE;
                            }
                        }
                        else{
                            if(
$skipped_quote) $skipped_quote = FALSE;
                           
$in_quote = TRUE;
                        }
                    }
                    else if(
$string{$i} == ";" ){
                        if(
$in_quote){
                           
$product .= ";";
                        }
                        else{
                           
$product .= " ; ";
                        }
                    }
                    else{
                        if(
$in_quote){
                           
$in_quote = FALSE;
                           
$product .= $string{$i};
                        }
                        else{
                           
$product .= $string{$i};
                        }
                    }
                }
                return
$product;
            }
        }
       
$data = array();
        if( 
is_string($string) && ( stripos($string, "\n") !== FALSE )  ){
           
$data = explode("\n", parse_csv_aux($string) );
            foreach(
$data as $key => $row){
               
$columns = array();
               
//$row = strtr(  $row, array( "\";\"" => "\";\"", ";" => " ; " )  );
               
if( stripos($row, " ; ") !== FALSE ){
                   
$columns = explode( " ; ", $row );
                    if( !
is_array($columns) )$columns = array( strval($columns) );
                   
$data[$key] = $columns;
                }
            }
            return
$data;
        }
        else if( 
is_string($string) && ( stripos( ($string parse_csv_aux($string)), " ; ") !== FALSE )  ){
           
$columns = explode( " ; ", $string );
            if( !
is_array($columns) )$columns = array( strval($columns) );
            return array(
$columns);
        }
        else return
strval($string);
    }
/* end function parse_csv */
} /* end not function exists parse_csv */

if( !function_exists("store_csv") ){
    function
store_csv($data){
/* Author : Alexander Peev, posted at PHP.NET */
       
if( !function_exists("store_csv_aux") ){
            function
store_csv_aux( $string ){
               
$string = strtr( $string, array( "\n" => "" ) );
               
$product = "";
               
$in_quote = FALSE;
                for( 
$i = 0 ; $i < strlen($string) ; $i++  ){
                    if(
$string{$i} == "\"" ){
                        if(
$in_quote){
                           
$product .= "\"\"";
                        }
                        else{
                           
$product .= "\"\"\"";
                           
$in_quote = TRUE;
                        }
                    }
                    else if(
$string{$i} == ";" ){
                        if(
$in_quote){
                           
$product .= ";";
                        }
                        else{
                           
$product .= "\";";
                           
$in_quote = TRUE;
                        }
                    }
                    else{
                        if(
$in_quote){
                           
$product .= "\"";
                           
$in_quote = FALSE;
                           
$product .= $string{$i};
                        }
                        else{
                           
$product .= $string{$i};
                        }
                    }
                }
                if(
$in_quote)$product .= "\"";
                return
$product;
            }
        }
        if(!
is_array($data))return strval($data);
       
$passed_rows = FALSE;
       
$product = "";
        foreach(
$data as $row){
            if(
$passed_rows )$product .= "\n";
            if(
is_array($row) ){
               
$columns = "";
               
$passed_cols = FALSE;
                foreach(
$row as $column){
                    if(
$passed_cols )$columns .= ";";
                   
$columns .= store_csv_aux( $column );
                   
$passed_cols =TRUE;
                }
               
$product .= strval($columns);
            }
            else{
               
$product .= strtr( strval($row), array("\n" => "") );
            }
           
$passed_rows = TRUE;
        }
        return
$product;
    }
/* end function store_csv */
} /* end not function exists store_csv */

?>
justin at cam dot org
16.02.2007 7:25
There's a discussion of how to perform this task in the user notes for the split() function.
http://www.php.net/manual/en/function.split.php



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)