0

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.

Axel
  • 3,331
  • 11
  • 35
  • 58
  • _How to overcome this issue without iterating over the array afterwards and convert back each and every value manually_ You can't,a csv file is text and is treated as such. – AbraCadaver Dec 11 '20 at 16:48
  • 2
    I quite like [this](https://stackoverflow.com/a/15519928/1213708) workaround. – Nigel Ren Dec 11 '20 at 16:53
  • @AbraCadaver sure you are right. csv file is text but it can represent numeric values as well. Which doesn't mean that you are wrong when saying: "You can't"... – Axel Dec 11 '20 at 16:54
  • I meant, you will have to do something with the data after getting it from csv. @NigelRen workaround looks to be the best. Just write your own function that incorporates the get csv and json_decode. – AbraCadaver Dec 11 '20 at 16:57
  • Did you give up? – AbraCadaver Jan 10 '21 at 04:15
  • Not at all @AbraCadaver. I got my solution as well as a deeper understanding. Also left an answer... – Axel Jan 10 '21 at 12:03

2 Answers2

1

We can close this as a duplicate, but I'm bored. json_decode will decode into int, float and boolean and is faster than other approaches:

function str_getcsv_typed($string, $delimiter=",", enclosure='"', $escape = "\\") {
    return json_decode('[' . str_getcsv($string, $delimiter, enclosure, $escape) . ']');
}

function file_getcsv_typed($handle, $length=0, $delimiter=",", enclosure='"', $escape = "\\") {
    return json_decode('[' . fgetcsv($handle, $length, $delimiter, enclosure, $escape) . ']');
}

If you happen to have anything in your CSV that is in JSON object or array format then it might decode those as well, maybe not what you want. Also, if there are any illegal JSON characters or syntax it will fail as well.

AbraCadaver
  • 78,200
  • 7
  • 66
  • 87
  • Could you link to the dup? There are a lot of questions that are about parsing csv files and strings but actually non of them refer to the type issue. https://stackoverflow.com/questions/9593765/how-to-convert-array-values-from-string-to-int/15519928#15519928 is not particularly about csv. Thank you and +1 anyway... Don't like that you are bored... – Axel Dec 11 '20 at 17:33
  • That's the one unless someone finds a better one. – AbraCadaver Dec 11 '20 at 17:39
0

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;
Axel
  • 3,331
  • 11
  • 35
  • 58