note 86629 added to function.explode
| From: | jaimthorn at yahoo dot com | Date: | Mon, 27 Oct 2008 08:54:44 +0000 |
| Subject: | note 86629 added to function.explode | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-145792@lists.php.net to get a copy of this message | ||
Like tajhlande at gmail dot com, below, I needed a routine that could read CSV files without
problems. The typical way of doing this (i.e. exploding each line read from the CSV file)
doesn't work if some fields from the input are demarcated by quotes, and others aren't.
If the separator (or delimiter, in php explode terms) occurs inside a field that is demarcated by
quotes, the field will be split in two.
Tajhlande's solution works great; however, as Tajhlande already admitted, it doesn't
accept separators or (what I like to call) 'demarcators' that consist of multiple
characters. Also, looking at Tajhlande's solution, I got the feeling that faster solutions
should be possible. Indeed, according to one test I performed, the code below is twice as fast.
<?php
function explode_csv($sep, $string, $dem = '')
{
// The return value, an array of fields.
$retval = array();
if(! isset($sep{0}))
{
// If the separator is empty then the entire record consists of one field: the input $string
itself.
$retval[] = $string;
return $retval;
}
// Determine the lengths of the separator and demarcator here, to prevent (more) expensive
// strlen() calls inside the while-loop, below.
$sep_len = strlen($sep);
$dem_len = strlen($dem);
// Make sure at least one separator is found. This is a hack to make the code below a bit
simpler.
$string = trim($string . $sep);
while(isset($string{1})) // Is $string > $sep?
{
// If a demarcator is given then find its position; else its position is false (not found).
$dem_pos = $dem_len ? strpos($string, $dem) : false;
// A separator can always be found because it was added to $string, above.
$sep_pos = strpos($string, $sep);
if($dem_pos === false)
{
// If a demarcator is not found then the field is everything up to (but not including)
the separator.
$start = 0;
$end = $sep_pos;
$next = $sep_pos + $sep_len;
}
else
{
if($sep_pos < $dem_pos)
{
// If a separator was found before the demarcator then the field is everything up to
(but not including) the separator.
$start = 0;
$end = $sep_pos;
$next = $sep_pos + $sep_len;
}
else
{
// The demarcator was found before a separator; find the position where to start
looking for the closing demarcator.
$dem_pos += $dem_lem + 1;
// Look for the closing demarcator.
$dem_pos2 = strpos($string, $dem . $sep, $dem_pos);
if($dem_pos2 === false)
{
// If a closing demarcator is not found then assume that the rest of the input
// (except for the separator that was added, above!) is a field.
$dem_pos2 = strlen($string) - $sep_len;
}
$start = $dem_pos;
$end = $dem_pos2 - $dem_pos;
$next = $dem_pos2 + $dem_len + $sep_len;
}
}
$retval[] = trim(substr($string, $start, $end));
// Remove the field plus the separator from the input.
$string = trim(substr($string, $next));
}
return $retval;
}
?>
This routine is intended to be a drop-in for existing explode() calls; just change the name of each
call into explode_csv() and it should work --although no warranties are given!
Note that the routine above assumes a relatively well-behaved input. Without actually having tested
it, I'm pretty sure that this input
Hello, this is "a field"
will be parsed and returned as an array of two fields:
"Hello", "a field"
I'm using this routine on files that are produced by OpenOffice Calc or Excel and I
assume/assert that these don't produce ill-behaved CSV files, so this is acceptable behavior to
me.
----
Server IP: 89.248.170.61
Probable Submitter: 213.93.155.227
----
Manual Page -- http://www.php.net/manual/en/function.explode.php
Edit -- https://master.php.net/note/edit/86629
Del: integrated -- https://master.php.net/note/delete/86629/integrated
Del: useless -- https://master.php.net/note/delete/86629/useless
Del: bad code -- https://master.php.net/note/delete/86629/bad+code
Del: spam -- https://master.php.net/note/delete/86629/spam
Del: non-english -- https://master.php.net/note/delete/86629/non-english
Del: in docs -- https://master.php.net/note/delete/86629/in+docs
Del: other reasons-- https://master.php.net/note/delete/86629
Reject -- https://master.php.net/note/reject/86629
Search -- https://master.php.net/manage/user-notes.php