phpcsvtype-conversionnumbersfgetcsv

Keep Integer and Float Types When Converting CSV Data to PHP Arrays With str_getcsv and fgetcsv


When using PHPs str_getcsv-function numbers will be treated as strings:

<?php
$csv = str_getcsv('0,1.2,"string"');
var_dump( $csv );
/* result: array(3) {
  [0]=>string(1) "0"
  [1]=>string(3) "1.2"
  [2]=>string(6) "string"
} */
/* DESIRED result: array(3) {
  [0]=>int(0)
  [1]=>float(1.2)
  [2]=>string(6) "string"
} */

The same is true for the fgetcsv-function. How to overcome this issue without iterating over the array afterwards and convert back each and every value manually - so to say.

Just to make it very clear - I'm not after a workaround like:

<?php
$csv = str_getcsv('0,1.2,"string"');
foreach ( $csv as &$v ) if ( is_numeric( $v ) ) $v += 0;
var_dump( $csv ); // desired result

as I'm dealing with quite a bit of data performance is a thing in my case.


Solution

  • Seems like unfortunately there is no other way than doing a workaround! There are some options to do it and there is not "the one way to go". Depending on each usecase there might be different solutions. For my situation I will use the following code.

    <?php
    $csv = '0,1.2,"string"';
    json_decode( "[$csv]", true );
    

    But be aware that this will fail with multiline values at least!

    Here are some tests:

    <?php
    $csv = '0,1.2,"string"';
    var_dump(
      json_decode( "[$csv]", true ) // works
      , str_getcsv( $csv )          // converts everything to string
    );
    /*
    array(3) { json_decode
      [0]=>int(0)
      [1]=>float(1.2)
      [2]=>string(6) "string"
    }
    array(3) { str_getcsv
      [0]=>
      string(1) "0"
      [1]=>
      string(3) "1.2"
      [2]=>
      string(6) "string"
    }
    */
    $csv = '", as value in ,-separated csv"';
    var_dump(
      json_decode( "[$csv]", true ) // works also when separator is part of value
      , str_getcsv( $csv )          // works also when separator is part of value but converts everything to string
    );
    /*
    array(1) { json_decode
      [0]=>string(29) ", as value in ,-separated csv"
    }
    array(1) { str_getcsv
      [0]=>string(29) ", as value in ,-separated csv"
    }
    */
    $csv = '0,"value
    with multiline"';
    var_dump(
      json_decode( "[$csv]", true ) // doesn't work as JSON can't handle multilines
      , str_getcsv( $csv )          // works also with multilines but converts everything to string
    );
    /*
    NULL json_decode
    array(2) { str_getcsv
      [0]=>string(1) "0"
      [1]=>string(21) "value
                  with multiline"
    }
    */
    

    A very robust way seems to be the version from my original question as it relies on the native str_getcsv-function and converts to appropriate types back afterwards. Performancewise this doesn't sound ideal to me but this should be only a concern when dealing with very heavy strings (which is the case for me).

    <?php
    $csv = str_getcsv('0,1.2,"string"');
    foreach ( $csv as &$v ) if ( is_numeric( $v ) ) $v += 0;