Formatted string parsing with conflicting delimiter - php

Whats the quickest/easiest way to parse a string delimited by \ and with known keywords?
$str = 'key1\0815\key2\some string here\key3\string with \ in between\key4\0
I need to have them in an array (key => value) for later use, but how do I manage to split the string when some values might contain a \?
The keywords are known, but they are all unique.
explode() would fail on that and sscanf() would be a hughe amount of manual editing

Use preg_split() to achieve this:
$arr = preg_split('/\\\\(?!\s)/', $str);
$result = array();
foreach (array_chunk($arr, 2) as $sub) {
list($key, $value) = $sub;
$result[$key] = $value;
}
Output:
Array
(
[keyword1] => 0815
[keyword2] => some string here
[keyword3] => string with \ in between
[keyword4] => 0
)
Demo

This should do it:
$result = array_combine(
$keys = preg_grep('/key[\d]+/',
$parts = preg_split('/(?:\\\\)?(key[\d]+)\\\\/', $str, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)),
$vals = array_diff($parts, $keys));

Related

How to convert a certain type of string into an array with keys in php?

I have a string of this type:
string(11) "2=OK, 3=OK"
from a text file. But I would like to convert it into an array of keys this type :
array (
[2] => Ok
[3] => Ok
)
I was wondering how we could do that in PHP.
Note:- I normally use explode() and str_split() for the conversions string into array but in this case I don't know how to do it.
use explode(), foreach() along with trim()
<?php
$string = "2=OK, 3=OK" ;
$array = explode(',',$string);
$finalArray = array();
foreach($array as $arr){
$explodedString = explode('=',trim($arr));
$finalArray[$explodedString[0]] = $explodedString[1];
}
print_r($finalArray);
https://3v4l.org/ZsNY8
Explode the string by ',' symbol. You will get an array like ['2=OK', ' 3=OK']
Using foreach trim and explode each element by '=' symbol
You can use default file reading code and traverse it to achieve what you want,
$temp = [];
if ($fh = fopen('demo.txt', 'r')) {
while (!feof($fh)) {
$temp[] = fgets($fh);
}
fclose($fh);
}
array_walk($temp, function($item) use(&$r){ // & to change in address
$r = array_map('trim',explode(',', $item)); // `,` explode
array_walk($r, function(&$item1){
$item1 = explode("=",$item1); // `=` explode
});
});
$r = array_column($r,1,0);
print_r($r);
array_walk — Apply a user supplied function to every member of an array
array_map — Applies the callback to the elements of the given arrays
explode — Split a string by a string
Demo.
You can use preg_match_all along with array_combine, str_word_count
$string = "2=OK, 3=OK" ;
preg_match_all('!\d+!', $string, $matches);
$res = array_combine($matches[0], str_word_count($string, 1));
Output
echo '<pre>';
print_r($res);
Array
(
[2] => OK
[3] => OK
)
LIVE DEMO

Why isn't php explode extracting array values

I have some date values (each with a trailing = sign) stored as a &-delimited string.
My $_POST['dates'] string looks like this:
23.08.18=&22.08.18=&21.08.18=
I'm trying the following PHP code to extract the dates before inserting them into my database table. The code is currently not working.
foreach(explode('&', $_POST['dates']) as $value)
{
$value1 = explode('=', $value);
foreach ($value1 as $x) {
}
}
How can I isolate the date values?
The function that you might be looking for is parse_str(). You have a valid query string (well, kind of) with keys but no values, so you merely need to parse it and isolate the keys(dates). Unfortunately, the parser converts your dots to underscores.
Code: (Demo)
$string = '23.08.18=&22.08.18=&21.08.18=';
parse_str($string, $out);
var_export(array_keys($out));
Output:
array (
0 => '23_08_18',
1 => '22_08_18',
2 => '21_08_18',
)
And of course, you can repair the modified date values with str_replace(). See this demo.
Or if you want to disregard parse_str(), you can use just one regex call that divides the string on = followed by an optional &:
Code: (Demo)
$string = '23.08.18=&22.08.18=&21.08.18=';
var_export(preg_split('~=&?~', $string, -1, PREG_SPLIT_NO_EMPTY));
Output:
array (
0 => '23.08.18',
1 => '22.08.18',
2 => '21.08.18',
)
Or without regex, just trim the last = and explode on =&: (Demo)
$string = '23.08.18=&22.08.18=&21.08.18=';
var_export(explode('=&', rtrim($string, '=')));
// same output as preg_split()
<?php
$data = '23.08.18=&22.08.18=&21.08.18=';
//$delimiters has to be array
//$string has to be array
function multiexplode ($delimiters,$string) {
$ary = explode($delimiters[0],$string);
array_shift($delimiters);
if($delimiters != NULL) {
foreach($ary as $key => $val) {
$ary[$key] = multiexplode($delimiters, $val);
}
}
return $ary;
}
$exploded = multiexplode(array("=","&"),$data);
var_dump($exploded);
The result should be:
array (size=3)
0 => string '23.08.18' (length=8)
2 => string '22.08.18' (length=8)
4 => string '21.08.18' (length=8)
We need to use array_filter.
http://php.net/manual/en/function.explode.php
[EDIT] mickmackusa's answer is the right tool for parse url parameters into variables.

explode string on multiple words

There is a string like this:
$string = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
This string is from user Input. So it will never have the same order. It's an input field which I need to split to build a DB query.
Now I would like to split the string based on words given in a array() which is like a mapper containing the words I need to find in the string. Looking like so:
$mapper = array(
'connector' => array('type' => 'string'),
'direction' => array('type' => 'string'),
'message' => array('type' => 'string'),
'date' => array('type' => 'date'),
);
Only the keys of the $mapper will be relevant. I've tried with foreach and explode like:
$parts = explode(':', $string);
But the problem is: There can be colons somewhere in the string so I don't need to explode there. I only need to explode if a colon is followed right after the mapper key. The mapper keys in this case are:
connector // in this case split if "connector:" is found
direction // untill "direction:" is found
message // untill "message:" is found
date // untill "date:" is found
But remember also, the user input can varey. So the string will always change ant the order of the string and the mapper array() will never be in the same order. So I'm not sure if explode is the right way to go, or if I should use a regex. And if so how to do it.
The desired result should be an array looking something like this:
$desired_result = array(
'connector' => 'rtp-monthly',
'direction' => 'outbound',
'message' => 'error writing data: xxxx yyyy zzzz',
'date' => '2015-11-02 10:20:30',
);
Help is much appreciated.
The trickier part of this is matching the original string. You can do it with Regex with the help of lookahead positive assertions:
$pattern = "/(connector|direction|message|date):(.+?)(?= connector:| direction:| message:| date:|$)/";
$subject = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER );
$returnArray = array();
foreach($matches as $item)
{
$returnArray[$item[1]] = $item[2];
}
In this Regex /(connector|direction|message|date):(.+?)(?= connector:| direction:| message:| date:|$)/, you're matching:
(connector|direction|message|date) - find a keyword and capture it;
: - followed by a colon;
(.+?) - followed by any character many times non greedy, and capture it;
(?= connector:| direction:| message:| date:|$) - up until the next keyword or the end of the string, using a non-capturing look-ahead positive assertion.
The result is:
Array
(
[connector] => rtp-monthly
[direction] => outbound
[message] => error writing data: xxxx yyyy zzzz
[date] => 2015-11-02 10:20:30
)
I didn't use the mapper array just to make the example clear, but you could use implode to put the keywords together.
Our aim isto make one array that contains the values of two arrays that we would extract from the string. It is neccesary to have two arrays since there are two string delimeters we wish to consider.
Try this:
$parts = array();
$large_parts = explode(" ", $string);
for($i=0; $i<count($large_parts); $i++){
$small_parts = explode(":", $large_parts[$i]);
$parts[$small_parts[0]] = $small_parts[1];
}
$parts should now contain the desired array
Hope you get sorted out.
Here you are. The regex is there to "catch" the key (any sequence of characters, excluding blank space and ":"). Starting from there, I use "explode" to "recursively" split the string. Tested ad works good
$string = 'connector:rtp-monthly direction:outbound message:error writing data date:2015-11-02';
$element = "(.*?):";
preg_match_all( "/([^\s:]*?):/", $string, $matches);
$result = array();
$keys = array();
$values = array();
$counter = 0;
foreach( $matches[0] as $id => $match ) {
$exploded = explode( $matches[ 0 ][ $id ], $string );
$keys[ $counter ] = $matches[ 1 ][ $id ];
if( $counter > 0 ) {
$values[ $counter - 1 ] = $exploded[ 0 ];
}
$string = $exploded[ 1 ];
$counter++;
}
$values[] = $string;
$result = array();
foreach( $keys as $id => $key ) {
$result[ $key ] = $values[ $id ];
}
print_r( $result );
You could use a combination of a regular expression and explode(). Consider the following code:
$str = "connector:rtp-monthly direction:outbound message:error writing data date:2015-11-02";
$regex = "/([^:\s]+):(\S+)/i";
// first group: match any character except ':' and whitespaces
// delimiter: ':'
// second group: match any character which is not a whitespace
// will not match writing and data
preg_match_all($regex, $str, $matches);
$mapper = array();
foreach ($matches[0] as $match) {
list($key, $value) = explode(':', $match);
$mapper[$key][] = $value;
}
Additionally, you might want to think of a better way to store the strings in the first place (JSON? XML?).
Using preg_split() to explode() by multiple delimiters in PHP
Just a quick note here. To explode() a string using multiple delimiters in PHP you will have to make use of the regular expressions. Use pipe character to separate your delimiters.
$string = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
$chunks = preg_split('/(connector|direction|message)/',$string,-1, PREG_SPLIT_NO_EMPTY);
// Print_r to check response output.
echo '<pre>';
print_r($chunks);
echo '</pre>';
PREG_SPLIT_NO_EMPTY – To return only non-empty pieces.

PHP: Split string into 2D array

I have a string which contains quite a bit of data. I want to split the data into a 2D array. The data in the string is split by a ~ (tilde) for the columns and a : (colon) for the different rows.
An example string could be: "London~10~20~cold:New York~23~53~hot:Madrid~43~12~dry".
Thanks.
$string = "London~10~20~cold:New York~23~53~hot:Madrid~43~12~dry";
$array = explode(':', $string);
foreach($array as &$value) $value = explode('~', $value);
Functional way (PHP 5.3.x needed):
$string = "London~10~20~cold:New York~23~53~hot:Madrid~43~12~dry";
$map = array_map(function($el) {
return explode('~', $el);
}, explode(':', $string));
Another alternative would be:
preg_match_all('/(.*?)~(.*?)~(.*?)~(.*?)(?:$|:)/', $string, $array,
PREG_SET_ORDER);
It's more cumbersome in that you have to predefine the column format. It also returns the complete match in each rows [0]. Otherwise (due to PREG_SET_ORDER) it's in your desired 2d format.
Just posting it here to please and annoy the microoptimizers at the same time. Despite the common Stackoverflow meme, the regex is three times faster than the explode loop.
You can split data in php with explode().
So first you have to split the string, than you have to split your entries again with explode().
$data = explode(':', $string);
$array = array();
foreach($data as $d) {
$d = explode('~', $d);
$array[] = $d; //e.g. $array[0][0] --> London
//$array[$d[0]] = array('temperature' => $d[1], 'dont-know' => $d[2], 'climate' => $d[3]); //e.g. $arra['London'] => array(...)
}
A "functional" style
$array = array_map(function($value) {
return explode('~', $value);
}, explode(':',$string););

php preg_match tab separated

I need a regular expression to look for the first N chars on an array until a tab or comma separation is found.
array look like:
array (
0 => '001,Foo,Bar',
1 => '0003,Foo,Bar',
2 => '3000,Foo,Bar',
3 => '3333433,Foo,Bar',
)
I'm looking for the first N chars, so for instance, pattern to search is 0003, get array index 1...
What would be a good way of doing this?
/^(.*?)[,\t]/
?
Try the regular expression /^0003,/ together with preg_grep:
$array = array('001,Foo,Bar', '0003,Foo,Bar', '3000,Foo,Bar', '3333433,Foo,Bar');
$matches = preg_grep('/^0003,/', $array);
var_dump($matches);
A REGEXP replacement would be: strpos() and substr()
Following your edit:
Use trim(), after searching for a comma with strpos() and retrieving the required string with substr().
use preg_split on the string
$length=10;
foreach($arr as $string) {
list($until_tab,$rest)=preg_split("/[\t,]+/", $string);
$match=substr($until_tab, $length);
echo $match;
}
or
array_walk($arr, create_function('&$v', 'list($v,$rest) = preg_split("/[\t,]+/", $string);'); //syntax not checked
This PHP5 code will do a prefix search on the first element, expecting a trailing comma. It's O(n), linear, inefficient, slow, etc. You'll need a better data structure if you want better search speed.
<?php
function searchPrefix(array $a, $needle) {
$expression = '/^' . quotemeta($needle) . ',/';
$results = array();
foreach ($a as $k => $v)
if (preg_match($expression, $v)) $results[] = $k;
return $results;
}
print_r(searchPrefix($a, '0003'));
$pattern = '/^[0-9]/siU';
for($i=0;$i<count($yourarray);$i++)
{
$ids = $yourarray[$i];
if(preg_match($pattern,$ids))
{
$results[$i] = $yourarray[$i];
}
}
print_r($results);
this will print
0 => '001',
1 => '0003',
2 => '3000',
3 => '3333433'

Categories