PHP regex to match key value pairs from a given string - php

i hope someone can help.
I have a string as following
$string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';
Now what i am trying is to create an array out of each key, value pair but badly failing with regex for preg_match_all()
Currently my attempts aren't giving desired results, creating key => value pairs works as long as there are no brackets, but i have absolutely no idea how to achieve a multidimensional array if key contains key/value pairs inside brackets in example.
Array (
[0] => Array
(
[0] => latitude=46.6781471,
[1] => longitude=13.9709534,
[2] => options=[units=si,
[3] => lang=de,
)
[1] => Array
(
[0] => latitude
[1] => longitude
[2] => options=[units
[3] => lang
)
.. and so on
Where in the end i would like to achieve results as following.
Array (
[latitude] => 46.6781471
[longitude] => 13.9709534
[options] => Array
(
[units] => auto
[exclude] => hourly,minutely
)
)
I would appreciate any help or example how i can achieve this from a given string.

Regular expression isn't the right tool to deal with recursive matches. You can write a parser instead of a regex (or use JSON, query string, XML or any other commonly used format):
function parseOptionsString($string) {
$length = strlen($string);
$key = null;
$contextStack = array();
$options = array();
$specialTokens = array('[', ']', '=', ',');
$buffer = '';
$currentOptions = $options;
for ($i = 0; $i < $length; $i++) {
$currentChar = $string[$i];
if (!in_array($currentChar, $specialTokens)) {
$buffer .= $currentChar;
continue;
}
if ($currentChar == '[') {
array_push($contextStack, [$key, $currentOptions]);
$currentOptions[$key] = array();
$currentOptions = $currentOptions[$key];
$key = '';
$buffer = '';
continue;
}
if ($currentChar == ']') {
if (!empty($buffer)) {
if (!empty($key)) {
$currentOptions[$key] = $buffer;
} else {
$currentOptions[] = $buffer;
}
}
$contextInfo = array_pop($contextStack);
$previousContext = $contextInfo[1];
$thisKey = $contextInfo[0];
$previousContext[$thisKey] = $currentOptions;
$currentOptions = $previousContext;
$buffer = '';
$key = '';
continue;
}
if ($currentChar == '=') {
$key = $buffer;
$buffer = '';
continue;
}
if ($currentChar == ',') {
if (!empty($key)) {
$currentOptions[$key] = $buffer;
} else if (!empty($buffer)) {
$currentOptions[] = $buffer;
}
$buffer = '';
$key = '';
continue;
}
}
if (!empty($key)) {
$currentOptions[$key] = $buffer;
}
return $currentOptions;
}
this gives the following output:
print_r(parseOptionsString($string));
Array
(
[latitude] => 46.6781471
[longitude] => 13.9709534
[options] => Array
(
[units] => auto
[lang] => de
[exclude] => Array
(
[0] => hourly
[1] => minutely
)
)
)
Note also that you want a special syntax for arrays with only comma separated values (exclude=[hourly,minutely] becomes exclude => hourly,minutely and not exclude => array(hourly, minutely)). I think this is an inconsistency in your format and I wrote the parser with the "correct" version in mind.

If you don't want parser you can also try this code. It converts your string to JSON and decode to array. But as others said, I think you should try the approach with JSON. If you're sending this string by XmlHttpRequest in JavaScript it will not be hard to create proper JSON code to send.
$string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';
$string = preg_replace('/([^=,\[\]\s]+)/', '"$1"', $string);
$string = '{' . $string . '}';
$string = str_replace('=', ':', $string);
$string = str_replace('[', '{', $string);
$string = str_replace(']', '}', $string);
$string = preg_replace('/({[^:}]*})/', '|$1|', $string);
$string = str_replace('|{', '[', $string);
$string = str_replace('}|', ']', $string);
$result = json_decode($string, true);
print_r($result);

Related

Replace comma within partial string [duplicate]

This question already has answers here:
PHP CSV string to array
(10 answers)
Closed 6 years ago.
I have a string like this:
'test', 'test', 'test, test', NULL, NULL, NULL, 123456789012, 0, '2017-02-17', FALSE
I want to explode it into an array.
But this gets messed up when a partial string contains a comma ('test, test').
How can I replace commas within a partial string into some other character? (so explode will work).
Apostrophes in the string must be contained, so str_getcsv() can not be used.
Here's my way:
$string = "'test', 'test', 'test, test, kk', NULL, NULL, NULL, 123456789012, 0, '2017-02-17', FALSE";
$array_tmp = explode(', ', $string);
$array = array();
$index_buffer = NULL;
$index = 0;
foreach($array_tmp as $value) {
// Check if we need to append to buffered entry
if($index_buffer !== NULL){
$array[$index_buffer] .= ', ' . $value;
if($value[strlen($value) - 1] === "'"){
$index_buffer = NULL;
}
continue;
}
// Check if it's not ended string
if(is_string($value) && $value[0] === "'" && $value[strlen($value) - 1] !== "'"){
// It is not ended, set this index as buffer
$index_buffer = $index;
}
// Save value
$array[$index] = $value;
$index++;
}
echo '<pre>' . print_r($array, true);
Output:
Array
(
[0] => 'test'
[1] => 'test'
[2] => 'test, test, kk'
[3] => NULL
[4] => NULL
[5] => NULL
[6] => 123456789012
[7] => 0
[8] => '2017-02-17'
[9] => FALSE
)
Or this may be more suitable but you lose quotes, and I guess, if your input string doesn't respect all csv standards, you could have border effects since str_getcsv handles more things than this quote issue:
str_getcsv($string, ",", "'");
You can do this manually and improve it to support more cases... Try something like this:
$arr = array();
$arr[0] = "";
$arrIndex = 0;
$strOpen = false;
for ($i = 0; $i < mb_strlen($str); $i++){
if ($str[$i] == ',') {
if ($strOpen == false) {
$arrIndex++;
$arr[$arrIndex] = "";
}
else {
$arr[$arrIndex] .= $str[$i];
}
}
else if ($str[$i] == '\'') {
$strOpen = !$strOpen;
}
else {
$arr[$arrIndex] .= $str[$i];
}
}
Result:
Array
(
[0] => test
[1] => test
[2] => test, test
[3] => NULL
[4] => NULL
[5] => NULL
[6] => 123456789012
[7] => 0
[8] => 2017-02-17
[9] => FALSE
)
Note: it will keep "empty" spaces around comas
Try using str_getcsv
str_getcsv($string, ",", "'");

Convert textarea to array and indented text to nested-arrays in PHP

I am wanting to take text input from a Form Textarea POST in PHP and iterate over each line of text and create a PHP array with the text.
The catch is I want to take lines of text that are indented with 4 spaces before them and make those lines be a sub nested array under the above array item.
I am not sure how to do this at the moment so would appreciate any ideas on it.
// basic demo to show each line of textarea post
$text = $_POST['textarea'];
foreach(explode("\n", $text) as $line) {
echo $line;
echo '<br>';
}
UPDATE
An idea so far something along the lines of this as a start maybe....
$text = $_POST['textarea'];
$in_nested_array = false;
$array = array();
foreach(explode("\n", $text) as $line) {
if($line is 4 spaces){
$in_nested_array = true;
$array[''][$line];
}else{
//if in nested array and new line is not nested, add to root array
if($in_nested_array){
}else{
$in_nested_array = false;
$array[] = $line;
}
}
}
Just a first try:
$str = <<<EOD
bar
baz
meh
lol
tuuut
moo
EOD;
function parse($lines, $depth = 0, $cur = 0)
{
$retVal = array();
for ($i = $cur; $i < count($lines); $i++)
{
$line = $lines[$i];
$lDepth = strlen($line) - strlen(ltrim($line, " "));
if ($lDepth == $depth)
{
$retVal[] = array("line" => ltrim($line, " "));
} elseif ($lDepth == $depth + 4) {
$children = parse($lines, $depth + 4, $i);
$retVal[count($retVal) - 1]["children"] = $children;
$i += count($children);
}
}
return $retVal;
}
$lines = explode("\n", $str);
echo "<pre>";
print_r(parse($lines));
Output:
Array
(
[0] => Array
(
[line] => bar
[children] => Array
(
[0] => Array
(
[line] => baz
[children] => Array
(
[0] => Array
(
[line] => meh
)
[1] => Array
(
[line] => lol
[children] => Array
(
[0] => Array
(
[line] => tuuut
)
)
)
)
)
)
)
[1] => Array
(
[line] => moo
)
)
To seperate by 4 spaces:
$txt = preg_split('/ +/', $text);

Get array from string in pattern - key1:val1,val2,..;key2:val1,

I would like to get from a string like this
color:blue,red;size:s
to an associative multiarray
[
color => [blue,red],
size => [s]
]
I tried with ([a-z]+):([a-z^,]+) but it's not enough; I don't know how to recursive it or something.
I wouldn't use regular expressions for something like this. Instead use explode() several times.
<?php
$str = 'color:blue,red;size:s';
$values = explode(';', $str);
$arr = [];
foreach($values as $val) {
$parts = explode(':', $val);
$arr[$parts[0]] = explode(',', $parts[1]);
}
Output:
Array
(
[color] => Array
(
[0] => blue
[1] => red
)
[size] => Array
(
[0] => s
)
)
$dataText = 'color:blue,red;size:s';
$data = explode(';', $dataText);
$outputData = [];
foreach ($data as $item){
$itemData = explode(':', $item);
$outputData[$itemData[0]] = explode(',', $itemData[1]);
}
print_r('<pre>');
print_r($outputData);
print_r('</pre>');
With regex is not so simple like explode, but you can try this...
$re = '/(\w+)\:([^;]+)/';
$str = 'color:blue,red;size:s';
preg_match_all($re, $str, $matches);
// Print the entire match result
$result = array();
$keys = array();
for($i = 1; $i < count($matches); $i++) {
foreach($matches[$i] as $k => $val){
if($i == 1) {
$result[$val] = array();
$keys[$k] = $val;
} else {
$result[$keys[$k]] = $val;
}
}
}
echo '<pre>';
print_r($result);
echo '</pre>';
result
Array
(
[color] => blue,red
[size] => s
)

PHP reading data input from a text file

I have a text file which I must read, and use the data from.
3
sam 99912222
tom 11122222
harry 12299933
sam
edward
harry
How can I create an array of these strings in the following form?
array(
"name" => "number"
...
)
I tried this:
$handle = fopen("file.txt", "r");
fscanf($handle, "%d %d", $name, $number);
What then? No matter what I try, it only works for the first line.
sam 99912222
Added codes to have both types of output - ignoring and including the lines that don't have name-value pairs. Check them out below
This code goes through each line and gets only the ones that have both name and value (something[space]something)):
//$lines = ... each line of the file in an array
$vals = array();
foreach($lines as $v){
$tmp = explode(' ', $v);
if(count($tmp) > 1){
$vals[trim($tmp[0])] = trim($tmp[1]); // trim to prevent garbage
}
}
print_r($vals);
It will output this:
Array
(
[sam] => 99912222
[tom] => 11122222
[harry] => 12299933
)
See the code in action here.
If you need the values even if they didn't come in pairs, do it like this:
//$lines = ... each line of the file
$vals = array();
foreach($lines as $v){
$tmp = explode(' ', $v);
$name = '';
$number = '';
$tmp[0] = trim($tmp[0]);
if(count($tmp) > 1){
$name = $tmp[0];
$number = trim($tmp[1]);
}else{
if(is_numeric($tmp[0])){
$number = $tmp[0];
}else{
$name = $tmp[0];
}
}
$vals[] = array(
'name' => $name,
'number' => $number
);
}
print_r($vals);
And the output:
Array
(
[0] => Array
(
[name] =>
[number] => 3
)
[1] => Array
(
[name] => sam
[number] => 99912222
)
[2] => Array
(
[name] => tom
[number] => 11122222
)
[3] => Array
(
[name] => harry
[number] => 12299933
)
[4] => Array
(
[name] => sam
[number] =>
)
[5] => Array
(
[name] => edward
[number] =>
)
[6] => Array
(
[name] => harry
[number] =>
)
See the code in action here.
Data in file are inconsistent, best of is to use regex to identify what data you've got from each line.
$lines = file('file.txt'); // this will open file and split them into lines
$items = array();
foreach($lines as $line){
$name = null;
$number = null;
$nameFound = preg_match("|([A-Za-z]+)|", $line, $matches);
if($nameFound){
$name = $matches[0];
}
$numberFound = preg_match("|([0-9]+)|", $line, $matches);
if($numberFound){
$number = $matches[0];
}
$items[] = array('name' => $name, 'number' => $number);
}
Then in items you should find parsed data from file.
To make it just extract full format data just change lines with regex into one line like this:
$lines = file('file.txt'); // this will open file and split them into lines
$items = array();
foreach($lines as $line){
$userFound = preg_match("/([A-Za-z]+) ([0-9]+)/", $line, $matches);
if($userFound){
$items[$matches[1]] = $matches[2];
}
}
With the Algorithm below, you can simply parse each individual line of the Text-File Contents into an array with the 1st Word or Digit(s) on each line as the Key and the 2nd Word as the Value. When the 2nd word or group of words do not exist, a NULL is assigned to that Key. For re-usability, this algorithm has been encapsulated into a Function. Here you go:
<?php
function parseTxtFile($txtFile){
$arrTxtContent = [];
if(!file_exists($txtFile)){ return null;}
$strFWriteTxtData = file_get_contents($txtFile);
if(empty($strFWriteTxtData)){return null;}
$arrFWriteInfo = explode("\n", $strFWriteTxtData);
foreach($arrFWriteInfo as $iKey=>$lineData){
$arrWriteData = explode(", ", $lineData);
foreach($arrWriteData as $intKey=>$strKeyInfo){
preg_match("#(^[a-z0-9_A-Z]*)(\s)(.*$)#i", $strKeyInfo, $matches);
preg_match("#(^[a-z0-9_A-Z]*)(\s*)?$#i", $strKeyInfo, $matches2);
if($matches) {
list(, $key, $null, $val) = $matches;
if (!array_key_exists($key, $arrTxtContent)) {
$arrTxtContent[$key] = $val;
}else{
$iKey = $intKey + 1;
$key = $key . "_{$iKey}";
$arrTxtContent[$key] = $val;
}
}else if($matches2) {
list(, $key, $null) = $matches2;
if (!array_key_exists($key, $arrTxtContent)) {
$arrTxtContent[$key] = null;
}else{
$key = preg_match("#_\d+#", $key, $match)? $key . $match[0] : "{$key}_1";
$arrTxtContent[$key] = null;
}
}
}
}
return $arrTxtContent;
}
var_dump(parseTxtFile(__DIR__ . "/data.txt"));
Just call the function parseTxtFile($txtFile) passing it the path to your text File and it will return an Array that looks something like below:
array (size=7)
3 => null
'sam' => string '99912222' (length=8)
'tom' => string '11122222' (length=8)
'harry' => string '12299933' (length=8)
'sam_1' => null
'edward' => null
'harry_1' => null
Hope this could help a bit....
Cheers & Good-Luck ;-)

Creating strings of X length based on array

I would like to iterate over an array and from that array create a string.
However, each string needs to be of certain size (500 bytes).
So my array looks like:
Array
(
[0] => Array
(
[name] => shirt
[price] => 1.25
)
[1] => Array
(
[name] => car
[price] => 25.10
)
...
)
$str = "";
foreach($arr as $v) {
$str .= "<name>".$v['name']."</name>";
$str .= "<price>".$v['price']."</price>";
}
Output should be something like:
str1 = '<name>shirt</name><price>1.25</price><name>car</name><price>25.10</price>...' // until 500 bytes or less.
str2 = '<name>shirt</name><price>1.25</price><name>car</name><price>25.10</price>...' // until 500 bytes or less.
// I need complete tags. So I can't have a string that looks like:
str = '<name>flower</name><pri';
Save each segment to as less than 500 characters.
$xml = array();
$str = '';
foreach($arr as $v)
{
$temp = "<name>".$v['name']."</name>";
$temp .= "<price>".$v['price']."</price>";
if(mb_strlen($str . $temp) > 500)
{
$xml[] = $str;
$str = '';
}
$str = $temp;
}
$xml[] = $str;
print_r($xml);
str_split sounds like a good candidate.

Categories