I want to convert this string:
<5> 20825.24 </ 5> <7> 0.0 </ 7> <8> 0.0 </ 8>
to an array with key value
I would really appreciate the help
I tried different xmlParse strategies but it is not exactly an xml.
$xml = new \SimpleXMLElement("<5>20825.24</5><7>0.0</ 7><8>0.0</8>");
Response: String could not be parsed as XML
I expect an array like this:
[5=>20825.24,7=>0.0,8=>0.0]
<?php
$subject = '<5> 20825.24 <7> 0.0 <8> 0.0';
$pattern = '/\<(\d)\>\s+(\d+\.\d+)/u';
$result = preg_match_all($pattern,$subject,$output);
$numbers = $output[1];
$output = $output[2];
$outTotal = [];
foreach ($numbers as $key => $number) {
$outTotal[$number] = $output[$key];
}
var_dump($outTotal);
gives:
array(3) {
[5]=>
string(8) "20825.24"
[7]=>
string(3) "0.0"
[8]=>
string(3) "0.0"
}
As long as your output has numbers at the start of the XML tag name, it won't be valid XML. You may have to resort to using a regex to do the job.
This uses <(\d*)>(.*?)</~
which looks for a <
followed by digits, then the >
and captures everything up till the next </
. It then combines the values from capture group 1 (the tag name) and 2 (the value)...
$data = "<5>20825.24</5><7>0.0</ 7><8>0.0</8>";
preg_match_all("~<(\d*)>(.*?)</~", $data, $matches);
$output = array_combine($matches[1], $matches[2]);
print_r($output);
gives...
Array
(
[5] => 20825.24
[7] => 0.0
[8] => 0.0
)
Here, we can also use a regular expression and collect our desired numbers, then push it to a new array:
(?:<)(\d?)(?:>)(?:.+?)([0-9.]+)?
This expression might also work with nested elements.
$re = '/(?:<)(\d?)(?:>)(?:.+?)([0-9.]+)?/m';
$str = '<5> 20825.24 </ 5> <7> 0.0 </ 7> <8> 0.0 </ 8>';
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
$arr = array();
foreach ($matches as $key => $value) {
$arr[$value[1]] = $value[2];
}
var_dump($arr);
array(3) {
[5]=>
string(8) "20825.24"
[7]=>
string(3) "0.0"
[8]=>
string(3) "0.0"
}
jex.im visualizes regular expressions:
If you want to match the digits in the opening tag with the digits in the closing tag, you could use a regex make use of a backreference to the first capturing group:
<(\d+)>\h*\d+(?:\.\d+)?\h*</\h*\1>
Explanation
<
Match >
char(\d+)>
Capture in group 1 matching 1+ digits and match >
\h*
Match 0+ horizontal whitespace chars\d+(?:\.\d+)?
Match 1+ digits followed by an optional part to match a dot and 1+ digits\h*
Match 0+ horizontal whitespace chars<
Match <
/\h*
Match \
followed by 0+ horizontal whitespace chars\1>
Match backreference to group 1 using \1
and match >
For example:
$re = '~<(\d+)>\h*(\d+(?:\.\d+)?)\h*</\h*\1>~m';
$str = '<5> 20825.24 </ 5> <7> 0.0 </ 7> <8> 0.0 </ 8>';
preg_match_all($re, $str, $matches);
print_r(array_combine($matches[1], $matches[2]));
Result
Array
(
[5] => 20825.24
[7] => 0.0
[8] => 0.0
)
Note that now the array keys are 5, 7 and 8. Keep in mind that if you might use different data, that arrays can not have duplicate keys.