Search code examples
phpcsv

PHP fgetcsv() delimiter ';' not recognized


it seems I have a problem with the ";" delimiter. Here's my csv file:

First Name;Last Name;Email;Age
Julie;Brown;julie@example.com;52
Dan;Wong;dan@example.com;19
Tim;Hortons;tim@example.com;27

and my PHP code:

$row = 1;
if (($handle = fopen("upload/".$_FILES['fichier']['name'], "r")) !== FALSE) {
    while (($data = fgetcsv($handle, ";")) !== FALSE) {
        $num = count($data);
        echo "<p> $num champs à la ligne $row: <br /></p>\n";
        $row++;
        for ($c=0; $c < $num; $c++) {
            echo $data[$c] . "<br />\n";
        }
    }
fclose($handle);
}

and I have this out put:

1 champs à la ligne 1: 
First Name;Last Name;Email;Age
1 champs à la ligne 2: 
Julie;Brown;julie@example.com;52
1 champs à la ligne 3: 
Dan;Wong;dan@example.com;19
1 champs à la ligne 4: 
Tim;Hortons;tim@example.com;27

instead of something like this when I use the ',' delimiter

4 champs à la ligne 1: 
First Name
Last Name
Email
Age

Besides, I want to know if it is possible to have various delimiters. Because I want to display csv files uploaded by users and I don't want to force them to use one predetermined delimiter.

Thanks


Solution

  • The second parameter is the length, so your fgetcsv should be

    fgetcsv($handle, 0, ';');
    

    Resulting in

    4 champs à la ligne 1: 
    
    First Name
    Last Name
    Email
    Age
    4 champs à la ligne 2: 
    
    Julie
    Brown
    julie@example.com
    52
    4 champs à la ligne 3: 
    
    Dan
    Wong
    dan@example.com
    19
    4 champs à la ligne 4: 
    
    Tim
    Hortons
    tim@example.com
    27
    

    As for your second question on variable delimiters. By far the easiest method would allow the user to define which delimiter to use on the upload form, possibly using a select element of acceptable delimiters and then use it when reading the csv.

    For Example

    function filter_delimiter($v) {
        return in_array($v, [',', ';'], true) ? $v : null;
    }
    
    // validate $_POST['delimiter'];
    $delimiter = filter_input(INPUT_POST, 'delimiter', FILTER_CALLBACK, ['options' => 'filter_delimiter']);
    if (!$delimiter) {
        $delimiter = ';';
        // optionally redirect to form error message
    }
    

    Alternatively parse the lines to determine the appropriate delimiter.

    // use absolute path
    $filename = __DIR__ . '/upload/' . $_FILES['fichier']['name'];
    if (false !== ($handle = fopen($filename, 'r'))) {
        $content = fread($handle, filesize($filename));
        fclose($handle);
        // count the delimiters
        $semiColons = substr_count($content, ';');
        $commas = substr_count($content, ',');
        // read each row
        $rows = str_getcsv($content, "\n");
        $rowCount = count($rows);
        $delimiter = null;
        foreach ($rows as $line => $row) {
            // check the delimiters
            if (!isset($delimiter)) {
                /* 
                  determine if the delimiter total divided by the number 
                  of rows matches the delimiters found on this row 
                  and use it to parse the columns 
                */
                if ($semiColons > 0 && $semiColons / $rowCount === substr_count($row, ';')) {
                    $delimiter = ';';
                } elseif ($commas > 0 && $commas / $rowCount === substr_count($row, ',')) {
                    $delimiter = ',';
                }
            }
            // read the columns using the detected delimiter 
            // otherwise use a default if a delimiter could not be determined
            $columns = str_getcsv($row, $delimiter ? : ';');
            echo "<p>$rowCount champs à la ligne " . ($line + 1) . "</p>\n";
            foreach ($columns as $column) {
                echo $column . "<br/>\n";
            }
        }
    }