I have trouble reading Postgresql arrays in PHP. I have tried explode(), but this breaks arrays containing commas in strings, and str_getcsv() but it's also no good as PostgreSQL doesn't quote the Japanese strings.
Not working:
explode(',', trim($pgArray['key'], '{}'));
str_getcsv( trim($pgArray['key'], '{}') );
Example:
// print_r() on PostgreSQL returned data: Array ( [strings] => {または, "some string without a comma", "a string, with a comma"} )
// Output: Array ( [0] => または [1] => "some string without a comma" [2] => "a string [3] => with a comma" )
explode(',', trim($pgArray['strings'], '{}'));
// Output: Array ( [0] => [1] => some string without a comma [2] => a string, with a comma )
print_r(str_getcsv( trim($pgArray['strings'], '{}') ));
If you have PostgreSQL 9.2 you can do something like this:
SELECT array_to_json(pg_array_result) AS new_name FROM tbl1;
The result will return the array as JSON
Then on the php side issue:
$array = json_decode($returned_field);
You can also convert back. Here are the JSON functions page
As neither of these solutions work with multidimentional arrays, so I offer here my recursive solution that works with arrays of any complexity:
function pg_array_parse($s, $start = 0, &$end = null)
{
if (empty($s) || $s[0] != '{') return null;
$return = array();
$string = false;
$quote='';
$len = strlen($s);
$v = '';
for ($i = $start + 1; $i < $len; $i++) {
$ch = $s[$i];
if (!$string && $ch == '}') {
if ($v !== '' || !empty($return)) {
$return[] = $v;
}
$end = $i;
break;
} elseif (!$string && $ch == '{') {
$v = pg_array_parse($s, $i, $i);
} elseif (!$string && $ch == ','){
$return[] = $v;
$v = '';
} elseif (!$string && ($ch == '"' || $ch == "'")) {
$string = true;
$quote = $ch;
} elseif ($string && $ch == $quote && $s[$i - 1] == "\\") {
$v = substr($v, 0, -1) . $ch;
} elseif ($string && $ch == $quote && $s[$i - 1] != "\\") {
$string = false;
} else {
$v .= $ch;
}
}
return $return;
}
I haven't tested it too much, but looks like it works.
Here you have my tests with results:
var_export(pg_array_parse('{1,2,3,4,5}'));echo "\n";
/*
array (
0 => '1',
1 => '2',
2 => '3',
3 => '4',
4 => '5',
)
*/
var_export(pg_array_parse('{{1,2},{3,4},{5}}'));echo "\n";
/*
array (
0 =>
array (
0 => '1',
1 => '2',
),
1 =>
array (
0 => '3',
1 => '4',
),
2 =>
array (
0 => '5',
),
)
*/
var_export(pg_array_parse('{dfasdf,"qw,,e{q\"we",\'qrer\'}'));echo "\n";
/*
array (
0 => 'dfasdf',
1 => 'qw,,e{q"we',
2 => 'qrer',
)
*/
var_export(pg_array_parse('{,}'));echo "\n";
/*
array (
0 => '',
1 => '',
)
*/
var_export(pg_array_parse('{}'));echo "\n";
/*
array (
)
*/
var_export(pg_array_parse(null));echo "\n";
// NULL
var_export(pg_array_parse(''));echo "\n";
// NULL
P.S.: I know this is a very old post, but I couldn't find any solution for postgresql pre 9.2
Reliable function to parse PostgreSQL (one-dimensional) array literal into PHP array, using regular expressions:
function pg_array_parse($literal)
{
if ($literal == '') return;
preg_match_all('/(?<=^\{|,)(([^,"{]*)|\s*"((?:[^"\\\\]|\\\\(?:.|[0-9]+|x[0-9a-f]+))*)"\s*)(,|(?<!^\{)(?=\}$))/i', $literal, $matches, PREG_SET_ORDER);
$values = [];
foreach ($matches as $match) {
$values[] = $match[3] != '' ? stripcslashes($match[3]) : (strtolower($match[2]) == 'null' ? null : $match[2]);
}
return $values;
}
print_r(pg_array_parse('{blah,blah blah,123,,"blah \\"\\\\ ,{\100\x40\t\daő\ő",NULL}'));
// Array
// (
// [0] => blah
// [1] => blah blah
// [2] => 123
// [3] =>
// [4] => blah "\ ,{## daőő
// [5] =>
// )
var_dump(pg_array_parse('{,}'));
// array(2) {
// [0] =>
// string(0) ""
// [1] =>
// string(0) ""
// }
print_r(pg_array_parse('{}'));
var_dump(pg_array_parse(null));
var_dump(pg_array_parse(''));
// Array
// (
// )
// NULL
// NULL
print_r(pg_array_parse('{または, "some string without a comma", "a string, with a comma"}'));
// Array
// (
// [0] => または
// [1] => some string without a comma
// [2] => a string, with a comma
// )
If you can foresee what kind text data you can expect in this field, you can use array_to_string function. It's available in 9.1
E.g. I exactly know that my array field labes will never have symbol '\n'. So I convert array labes into string using function array_to_string
SELECT
...
array_to_string( labels, chr(10) ) as labes
FROM
...
Now I can split this string using PHP function explode:
$phpLabels = explode( $pgLabes, "\n" );
You can use any sequence of characters to separate elements of array.
SQL:
SELECT
array_to_string( labels, '<--###DELIMITER###-->' ) as labes
PHP:
$phpLabels = explode( '<--###DELIMITER###-->', $pgLabes );
As #Kelt mentioned:
Postgresql arrays look like this: {1,2,3,4}
You can just simply replace first { and last } with [ and ]
respectively and then json_decode that.
But his solution works only for one-dimensional arrays.
Here the solution either for one-dimensional and multidimensional arrays:
$postgresArray = '{{1,2},{3,4}}';
$phpArray = json_decode(str_replace(['{', '}'], ['[', ']'], $postgresArray)); // [[1,2],[3,4]]
To cast back:
$phpArray=[[1,2],[3,4]];
$postgresArray=str_replace(['[', ']'], ['{', '}'], json_encode($phpArray));
Based on the answers in the thread i created two simple php functions that can be of use:
private function pgArray_decode(string $pgArray){
return explode(',', trim($pgArray, '{}'));
}
private function pgArray_encode(array $array){
$jsonArray = json_encode($array, true);
$jsonArray = str_replace('[','{',$jsonArray);
$jsonArray = str_replace(']','}',$jsonArray);
return $jsonArray;
}
I tried the array_to_json answer, but unfortunalety this results in an unknown function error.
Using the dbal query builder on a postgres 9.2 database with something like ->addSelect('array_agg(a.name) as account_name'), I got as result a string like { "name 1", "name 2", "name 3" }
There are only quotes around the array parts if they contain special characters like whitespace or punctuation.
So if there are quotes, I make the string a valid json string and then use the build-in parse json function. Otherwise I use explode.
$data = str_replace(array("\r\n", "\r", "\n"), "", trim($postgresArray,'{}'));
if (strpos($data, '"') === 0) {
$data = '[' . $data . ']';
$result = json_decode($data);
} else {
$result = explode(',', $data);
}
If you have control of the query that's hitting the database, why don't you just use unnest() to get the results as rows instead of Postgres-arrays? From there, you can natively get a PHP-array.
$result = pg_query('SELECT unnest(myArrayColumn) FROM someTable;');
if ( $result === false ) {
throw new Exception("Something went wrong.");
}
$array = pg_fetch_all($result);
This sidesteps the overhead and maintenance-issues you'd incur by trying to convert the array's string-representation yourself.
I can see you are using explode(',', trim($pgArray, '{}'));
But explode is used to Split a string by string (and you are supplying it an array!!). something like ..
$string = "A string, with, commas";
$arr = explode(',', $string);
What are you trying to do with array? if you want to concatenate have a look on implode
OR not sure if it is possible for you to specify the delimiter other than a comma? array_to_string(anyarray, text)
Postgresql arrays look like this: {1,2,3,4}
You can just simply replace first { and last } with [ and ] respectively and then json_decode that.
$x = '{1,2,3,4}';
$y = json_decode('[' . substr($x, 1, -1) . ']'); // [1, 2, 3, 4]
To cast back the other way would be mirror opposite:
$y = [1, 2, 3, 4];
$x = '{' . substr(json_encode($y), 1, -1) . '}';
A simple and fast function for converting deep PostgreSQL array string to JSON string without using pg connection.
function pgToArray(string $subject) : array
{
if ($subject === '{}') {
return array();
}
$matches = null;
// find all elements;
// quoted: {"1{\"23\"},abc"}
// unquoted: {abc,123.5,TRUE,true}
// and empty elements {,,}
preg_match_all( '/\"((?<=\\\\).|[^\"])*\"|[^,{}]+|(?={[,}])|(?=,[,}])/', $subject,$matches,PREG_OFFSET_CAPTURE);
$subject = str_replace(["{","}"],["[","]"],$subject); // converting delimiters to JSON
$matches = array_reverse($matches[0]);
foreach ($matches as $match) {
$item = trim($match[0]);
$replace = null;
if ((strpos($item,"{") !== false) || (strpos($item,"}") !== false)) {
// restoring replaced '{' and '}' inside string
$replace = $match[0];
} elseif (in_array($item,["NULL","TRUE","FALSE"])) {
$replace = strtolower($item);
} elseif ($item === "" || ($item[0] !== '"' && !in_array($item,["null","true","false"]) && !is_float($item))) {
$replace = '"' . $item . '"'; // adding quotes to string element
}
if ($replace) { // concatenate modified element instead of old element
$subject = substr($subject, 0, $match[1]) . $replace . substr($subject, $match[1] + strlen($match[0]));
}
}
return json_decode($subject, true);
}
Related
Given a string that contains values separated by dots:
property.entry.item
What is the best way to convert that to a key for an associative array?
$result['imported_data']['property']['entry']['item']
The string may be of any length, with any number of dots and contain an value:
people.arizona.phoenix.smith
I've tried the following without success:
//found a dot, means we are expecting output from a previous function
if( preg_match('[.]',$value)) {
//check for previous function output
if(!is_null($result['import'])) {
$chained_result_array = explode('.',$value);
//make sure we have an array to work with
if(is_array($chained_result_array)) {
$array_key = '';
foreach($chained_result_array as $key) {
$array_key .= '[\''.$key.'\']';
}
}
die(print_r(${result.'[\'import\']'.$array_key}));
}
}
I was thinking I could convert the string to a variable variable, but I get an array to string conversion error.
You can explode the string into an array and loop through the array. (DEMO)
/**
* This is a test array
*/
$testArray['property']['entry']['item'] = 'Hello World';
/**
* This is the path
*/
$string = 'property.entry.item';
/**
* This is the function
*/
$array = explode('.', $string);
foreach($array as $i){
if(!isset($tmp)){
$tmp = &$testArray[$i];
} else {
$tmp = $tmp[$i];
}
}
var_dump( $tmp ); // output = Hello World
Split the string into parts, and itterate the array, accessing each element in turn:
function arrayDotNotation($array, $dotString){
foreach(explode('.', $dotString) as $section){
$array = $array[$section];
}
return $array;
}
$array = ['one'=>['two'=>['three'=>'hello']]];
$string = 'one.two.three';
echo arrayDotNotation($array, $string); //outputs hello
Live example: http://codepad.viper-7.com/Vu8Hhy
You should really check to see if keys exist before you reference them. Otherwise, you're going to spew a lot of warnings.
function getProp($array, $propname) {
foreach(explode('.', $propname) as $node) {
if(isset($array[$node]))
$array = &$array[$node];
else
return null;
}
return $array;
}
Now you can do things like:
$x = array(
'name' => array(
'first' => 'Joe',
'last' => 'Bloe',
),
'age' => 27,
'employer' => array(
'current' => array(
'name' => 'Some Company',
)
)
);
assert(getProp($x, 'age') == 27);
assert(getProp($x, 'name.first') == 'Joe');
assert(getProp($x, 'employer.current.name') == 'Some Company');
assert(getProp($x, 'badthing') === NULL);
assert(getProp($x, 'address.zip') === NULL);
Or, if you are only interested in the import section of the tree:
getProp($x['import'], 'some.path');
I have a string which looks like this:
15-02-01-0000
15-02-02-0000
15-02-03-0000
15-02-04-0000
15-02-05-0000
15-02-10-0000
15-02-10-9100
15-02-10-9101
15-15-81-0000
15-15-81-0024
So the expected output would be:
All account grouping separated by "-" dashes for example: 15-02-01-0000 there is 3 grouping
start with 15
start with 15-02
start with 15-02-01
So the expected output would be:
First it will show
15 --> All account start with "15"
15-02 --> All account start with "15-02"
15-02-01 -- All accounts start with "15-02-01"
15-02-01-0000
15-02-02 -- All accounts start with 15-02-02
15-02-02-0000
15-02-03 -- onwards like above
15-02-03-0000
15-02-04
15-02-04-0000
15-02-05
15-02-05-0000
15-02-10
15-02-10-0000
15-02-10-9100
15-02-10-9101
15-15
15-15-81
15-15-81-0000
15-15-81-0024
I tried to use substr:
$res = substr("15-15-81-0024",3,2);
if ($res == "15") {
} else if ($res < 10 && $res != 00) {
} else {
}
But not working to put grouping.
Could you please suggest any good way?
You can break each data by - and build the array in as much as needed. Notice the use of & in the code as using reference to result array.
Example:
$str = "15-02-01-0000,15-02-02-0000,15-02-03-0000,15-02-04-0000,15-02-05-0000,15-02-10-0000,15-02-10-9100,15-02-10-9101,15-15-81-0000,15-15-81-0024";
$arr = explode(",", $str);
$res = [];
foreach($arr as $e) { // for each line in your data
$a = explode("-", $e); //break to prefix
$current = &$res;
while(count($a) > 1) { // create the array to that specific place if needed
$key = array_shift($a); // take the first key
if (!isset($current[$key])) // if the path not exist yet create empty array
$current[$key] = array();
$current = &$current[$key];
}
$current[] = $e; // found the right path so add the element
}
The full result will be in $res.
I'd probably do something along the lines of:
Could be more efficient if more time was spent on it.
<?php
$random = '15-02-01-0000
15-02-02-0000
15-02-03-0000
15-02-04-0000
15-02-05-0000
15-02-10-0000
15-02-10-9100
15-02-10-9101
15-15-81-0000
15-15-81-0024';
$lines = explode(PHP_EOL, $random);
$accounts = return_count($lines);
var_dump($accounts);
function return_count($lines){
$count_accounts = array();
$possibilties = array();
if(is_array($lines) && !empty($lines)){
foreach($lines as $val){
$line = explode('-', $val);
array_push($possibilties, $line[0], $line[0] . '-' . $line[1], $line[0] . '-' . $line[1] . '-' . $line[2]);
}
foreach($possibilties as $pos){
if(!isset($count_accounts[$pos])){ $count_accounts[$pos] = 0;}
if(search_array($pos, $lines)){
$count_accounts[$pos]++;
}
}
}
return $count_accounts;
}
function search_array($string, $array){
$found = 0;
if(is_array($array) && !empty($array)){
foreach($array as $val){
if (strpos($val, $string) !== false) {
$found = 1;
}
}
if($found == 1){
return true;
}else{
return false;
}
}else{
return false;
}
}
?>
Which returns:
array (size=10)
15 => int 10
'15-02' => int 8
'15-02-01' => int 1
'15-02-02' => int 1
'15-02-03' => int 1
'15-02-04' => int 1
'15-02-05' => int 1
'15-02-10' => int 3
'15-15' => int 2
'15-15-81' => int 2
Given a string that contains values separated by dots:
property.entry.item
What is the best way to convert that to a key for an associative array?
$result['imported_data']['property']['entry']['item']
The string may be of any length, with any number of dots and contain an value:
people.arizona.phoenix.smith
I've tried the following without success:
//found a dot, means we are expecting output from a previous function
if( preg_match('[.]',$value)) {
//check for previous function output
if(!is_null($result['import'])) {
$chained_result_array = explode('.',$value);
//make sure we have an array to work with
if(is_array($chained_result_array)) {
$array_key = '';
foreach($chained_result_array as $key) {
$array_key .= '[\''.$key.'\']';
}
}
die(print_r(${result.'[\'import\']'.$array_key}));
}
}
I was thinking I could convert the string to a variable variable, but I get an array to string conversion error.
You can explode the string into an array and loop through the array. (DEMO)
/**
* This is a test array
*/
$testArray['property']['entry']['item'] = 'Hello World';
/**
* This is the path
*/
$string = 'property.entry.item';
/**
* This is the function
*/
$array = explode('.', $string);
foreach($array as $i){
if(!isset($tmp)){
$tmp = &$testArray[$i];
} else {
$tmp = $tmp[$i];
}
}
var_dump( $tmp ); // output = Hello World
Split the string into parts, and itterate the array, accessing each element in turn:
function arrayDotNotation($array, $dotString){
foreach(explode('.', $dotString) as $section){
$array = $array[$section];
}
return $array;
}
$array = ['one'=>['two'=>['three'=>'hello']]];
$string = 'one.two.three';
echo arrayDotNotation($array, $string); //outputs hello
Live example: http://codepad.viper-7.com/Vu8Hhy
You should really check to see if keys exist before you reference them. Otherwise, you're going to spew a lot of warnings.
function getProp($array, $propname) {
foreach(explode('.', $propname) as $node) {
if(isset($array[$node]))
$array = &$array[$node];
else
return null;
}
return $array;
}
Now you can do things like:
$x = array(
'name' => array(
'first' => 'Joe',
'last' => 'Bloe',
),
'age' => 27,
'employer' => array(
'current' => array(
'name' => 'Some Company',
)
)
);
assert(getProp($x, 'age') == 27);
assert(getProp($x, 'name.first') == 'Joe');
assert(getProp($x, 'employer.current.name') == 'Some Company');
assert(getProp($x, 'badthing') === NULL);
assert(getProp($x, 'address.zip') === NULL);
Or, if you are only interested in the import section of the tree:
getProp($x['import'], 'some.path');
I have several method to transform php array to csv string both from stackoverflow and google. But I am in trouble that if I want to store mobile number such as 01727499452, it saves as without first 0 value.
I am currently using this piece of code:
Convert array into csv
Can anyone please help me.
Array
[1] => Array
(
[0] => Lalu ' " ; \\ Kumar
[1] => Mondal
[2] => 01934298345
[3] =>
)
[2] => Array
(
[0] => Pritom
[1] => Kumar Mondal
[2] => 01727499452
[3] => Bit Mascot
)
[3] => Array
(
[0] => Pritom
[1] => Kumar Mondal
[2] => 01711511149
[3] =>
)
[4] => Array
(
[0] => Raaz
[1] => Mukherzee
[2] => 01911224589
[3] => Khulna University 06
)
)
My code block:
function arrayToCsv( array $fields, $delimiter = ';', $enclosure = '"', $encloseAll = false, $nullToMysqlNull = false ) {
$delimiter_esc = preg_quote($delimiter, '/');
$enclosure_esc = preg_quote($enclosure, '/');
$outputString = "";
foreach($fields as $tempFields) {
$output = array();
foreach ( $tempFields as $field ) {
if ($field === null && $nullToMysqlNull) {
$output[] = 'NULL';
continue;
}
// Enclose fields containing $delimiter, $enclosure or whitespace
if ( $encloseAll || preg_match( "/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field ) ) {
$field = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure;
}
$output[] = $field." ";
}
$outputString .= implode( $delimiter, $output )."\r\n";
}
return $outputString; }
Thanks,
Pritom.
You could use str_putcsv function:
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 = fread($fp, 1048576);
// ... close the "file"...
fclose($fp);
// ... and return the $data to the caller, with the trailing newline from fgets() removed.
return rtrim($data, "\n");
}
}
$csvString = '';
foreach ($list as $fields) {
$csvString .= str_putcsv($fields);
}
More about this on GitHub, a function created by #johanmeiring.
This is what you need
$out = "";
foreach($array as $arr) {
$out .= implode(",", $arr) . PHP_EOL;
}
It runs through your array creating a new line on each loop seperating the array values with a ",".
Inspired by #alexcristea's answer:
function array2csv($data, $delimiter = ',', $enclosure = '"', $escape_char = "\\")
{
$f = fopen('php://memory', 'r+');
foreach ($data as $item) {
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
}
rewind($f);
return stream_get_contents($f);
}
$list = array (
array('aaa', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('"aaa"', '"bbb"')
);
var_dump(array2csv($list));
Are you sure the numbers are actually being saved without the leading zero? Have you looked at the actual CSV output in a text editor?
If you've just opened up the CSV file in a spreadsheet application, it is most likely the spreadsheet that is interpreting your telephone numbers as numeric values and dropping the zeros when displaying them. You can usually fix that in the spreadsheet by changing the formatting options on that particular column.
Since it's a CSV and not something like JSON, everything is going to be read as a string anyway so just convert your number to a string with:
(string)$variable
strval($variable) (which I would prefer here)
concatenate with an empty string like $variable . ''
PHP's gettype() would also be an option. You can type cast every field to a string (even if it already is one) by using one of the methods I described or you can call out just the number field you're after by doing this:
if (gettype($field) == 'integer' || gettype($field) == 'double') {
$field = strval($field); // Change $field to string if it's a numeric type
}
Here's the full code with it added
function arrayToCsv( array $fields, $delimiter = ';', $enclosure = '"', $encloseAll = false, $nullToMysqlNull = false ) {
$delimiter_esc = preg_quote($delimiter, '/');
$enclosure_esc = preg_quote($enclosure, '/');
$outputString = "";
foreach($fields as $tempFields) {
$output = array();
foreach ( $tempFields as $field ) {
// ADDITIONS BEGIN HERE
if (gettype($field) == 'integer' || gettype($field) == 'double') {
$field = strval($field); // Change $field to string if it's a numeric type
}
// ADDITIONS END HERE
if ($field === null && $nullToMysqlNull) {
$output[] = 'NULL';
continue;
}
// Enclose fields containing $delimiter, $enclosure or whitespace
if ( $encloseAll || preg_match( "/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field ) ) {
$field = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure;
}
$output[] = $field." ";
}
$outputString .= implode( $delimiter, $output )."\r\n";
}
return $outputString;
}
Here is a solution that is a little more general purpose. I was actually looking for a way to make string lists for SQL bulk inserts. The code would look like this:
foreach ($rows as $row) {
$string = toDelimitedString($row);
// Now append it to a file, add line break, whatever the need may be
}
And here is the useful function that I ended up adding to my tookit:
/**
* Convert an array of strings to a delimited string. This function supports CSV as well as SQL output since
* the quote character is customisable and the escaping behaviour is the same for CSV and SQL.
*
* Tests:
* echo toDelimitedString([], ',', '\'', true) . "\n";
* echo toDelimitedString(['A'], ',', '\'', true) . "\n";
* echo toDelimitedString(['A', 'B'], ',', '\'', true) . "\n";
* echo toDelimitedString(['A', 'B\'C'], ',', '\'', true) . "\n";
* echo toDelimitedString([], ',', '\'', true) . "\n";
* echo toDelimitedString(['A'], ',', '"', true) . "\n";
* echo toDelimitedString(['A', 'B'], ',', '"', true) . "\n";
* echo toDelimitedString(['A', 'B"C'], ',', '"', true) . "\n";
*
* Outputs:
* <Empty String>
* 'A'
* 'A','B'
* 'A','B''C'
* <Empty String>
* "A"
* "A","B"
* "A","B""C"
*
* #param array $array A one-dimensional array of string literals
* #param string $delimiter The character to separate string parts
* #param string $quoteChar The optional quote character to surround strings with
* #param bool $escape Flag to indicate whether instances of the quote character should be escaped
* #return string
*/
function toDelimitedString(array $array, string $delimiter = ',', string $quoteChar = '"', bool $escape = true)
{
// Escape the quote character, since it is somewhat expensive it can be suppressed
if ($escape && !empty($quoteChar)) {
$array = str_replace($quoteChar, $quoteChar . $quoteChar, $array);
}
// Put quotes and commas between all the values
$values = implode($array, $quoteChar . $delimiter . $quoteChar);
// Put first and last quote around the list, but only if it is not empty
if (strlen($values) > 0) {
$values = $quoteChar . $values . $quoteChar;
}
return $values;
}
This implementation does not use file pointers, shouldn't inadvertently modify any data passed to it, and only escapes as required, analogous to fputcsv() (but without the $escape_char nonsense):
function str_putcsv(array $fields, string $delimiter = ',', string $enclosure = '"') {
$escs = [];
foreach ($fields as $field) {
$esc = (string) $field;
if (
false !== strpos($esc, $delimiter)
|| false !== strpos($esc, $enclosure)
) {
$esc = $enclosure . strtr($esc, ["$enclosure" => "$enclosure$enclosure"]) . $enclosure;
}
$escs[] = $esc;
}
return implode($delimiter, $escs) . PHP_EOL;
}
Drop the string type declarations if your PHP version doesn't support them.
I'm using this function to convert php array to csv. It's working also for multidimensional array.
public function array_2_csv($array) {
$csv = array();
foreach ($array as $item=>$val) {
if (is_array($val)) {
$csv[] = $this->array_2_csv($val);
$csv[] = "\n";
} else {
$csv[] = $val;
}
}
return implode(';', $csv);
}
The function above is not exactly right cause it considers \n like an element, which is not what we want as each line must be only separated by \n. So a more efficient code would be:
function array2csv($array, $delimiter = "\n") {
$csv = array();
foreach ($array as $item=>$val)
{
if (is_array($val))
{
$csv[] = $this->array2csv($val, ";");
}
else
{
$csv[] = $val;
}
}
return implode($delimiter, $csv);
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Simulate php array language construct or parse with regexp?
suppose I have the string
$str = "array(1,3,4),array(array(4,5,6)),'this is a comma , inside a string',array('asdf' => 'lalal')";
and I try to explode this into an array by comma so that the desired end result is
$explode[0] = array(1,3,4);
$explode[1] = array(array(4,5,6));
$explode[2] = 'this is a comma , inside a string';
$explode[3] = array('asdf' => 'lalal');
simply calling explode(',',$str) is not going to cut it since there are also commas within those chunks...
is there a way to explode this reliably even if there is commas inside the desired chunks
is there a way to explode this reliably even if there is commas inside the desired chunks?
PHP by default does not provide such a function. However you have a compact subset of PHP inside your string and PHP offers some tools here: A PHP tokenizer and a PHP parser.
Therefore it's possible for your string specification to create a helper function that validates the input against allowed tokens and then parse it:
$str = "array(1,3,4),array(array(4,5,6)),'this is a comma , inside a string', array('asdf' => 'lalal')";
function explode_string($str)
{
$result = NULL;
// validate string
$isValid = FALSE;
$tokens = token_get_all(sprintf('<?php %s', $str));
array_shift($tokens);
$valid = array(305, 315, 358, 360, 371, '(', ')', ',');
foreach($tokens as $token)
{
list($index) = (array) $token;
if (!in_array($index, $valid))
{
$isValid = FALSE;
break;
}
}
if (!$isValid)
throw new InvalidArgumentException('Invalid string.');
// parse string
$return = eval(sprintf('return array(%s);', $str));
return $return;
}
echo $str, "\n";
$result = explode_string($str);
var_dump($result);
The tokens used are:
T_LNUMBER (305)
T_CONSTANT_ENCAPSED_STRING (315)
T_DOUBLE_ARROW (358)
T_ARRAY (360)
T_WHITESPACE (371)
The token index number can be given a token name by using token_name.
Which gives you (Demo):
Array
(
[0] => Array
(
[0] => 1
[1] => 3
[2] => 4
)
[1] => Array
(
[0] => Array
(
[0] => 4
[1] => 5
[2] => 6
)
)
[2] => this is a comma , inside a string
[3] => Array
(
[asdf] => lalal
)
)
You can write a simple parser:
function explode_str_arr($str) {
$str.=',';
$escape_char = '';
$str_len = strlen($str);
$cur_value = '';
$return_arr = array();
$cur_bracket_level = 0;
for ($i = 0; $i < $str_len; $i++) {
if ($escape_char) {
if ($str[$i] === $escape_char) {
$escape_char = '';
}
$cur_value.=$str[$i];
continue;
}
switch ($str[$i]) {
case '\'':
case '"':
$escape_char = $str[$i];
break;
case '(':
$cur_bracket_level++;
break;
case ')':
$cur_bracket_level--;
break;
case ',':
if (!$cur_bracket_level) {
$return_arr[] = $cur_value;
$cur_value = '';
continue 2;
}
}
$cur_value.=$str[$i];
}
return $return_arr;
}
It is ugly unicode-breaking fast code, but I think you may get the idea.