PHP Regex - match attribute and value in string - php

I have an array of possible attributes:
$attributes = ['color','size'];
Part of my URL looks like this:
color-light-grey-size-xs
I would need to get an array of attributes and their values, ie:
$values = [
'color' => 'light-grey',
'size' => 'xs'
]
Is that doable with regex?

A Regular Expression which you have to feed its cluster of attributes ORed:
(\w++)(?>-(\w+-?(?(?!color|size)(?-1))*))
^^^^^^^^^^
Regex live demo
PHP code:
$str = "color-light-grey-test-size-xs";
$attrs = ['color', 'size'];
$array = [];
preg_replace_callback(
"/(\w++)(?>-(\w+-?(?(?!" . implode("|", $attrs) . ")(?-1))*))/",
function($matches) use (&$array) {
$array[$matches[1]] = rtrim($matches[2], '-');
},
$str
);
print_r($array);
PHP live demo
Note: Order is not important at all.

I will tell you something.
ONLY if you know that first value is color and second value is size you can match between it like:
\bcolor\-([\w\d\-]+)-size-([\w\d\-]+)\b
You will get array of 2 matches for color $1 and for size $2
BUT if you don't know how your URL will look, you are in big problem.
You must know what you expect for all URL's and made matches for every combination.
Here is live example: https://regex101.com/r/3daBXx/1

Related

How to save variable inside a database to use later

I don't know what to call this definition problem. I need to parse value from preg_match_all. before i get the value i want. I need to parse again the multidimensional array value return by preg_match_all.
The point is I need to reuse code with different value. The different only in variable $regex and $match[1][0]; In my database I have 2 columns. Regex value is pattern regex to match. The second column is $match[1][0] variable. What is call the definition?
I don't want to pass value variable $match[1][0]. but the $match[1][0]. and use it later with loop.
<?php
$temporary = "$match[1][0]";
preg_match_all($regex, $source, $match);
$match = $temporary;
echo $match;
You can try eval() function。It run a string with php code!
You can like this
$match = [
1 => [
[1]
]
];
$temporary = '$match[1][0]';
$match = eval('return $match[1][0];');
var_dump($match);

How to extract only parameter value from URL using ONLY Regular Expressions

Extract the value of the u2 parameter from this URL using a regular expression. http://www.example.com?u1=US&u2=HA853&u3=HPA
<?php
$subject="http://www.example.com?u1=US&u2=HA853&u3=HPA"; //my url
$pattern='/u2=[0-9A-Za-z]*/'; //R.E that url value is only digit/Alphabet
preg_match($pattern,$subject,$match);
print_r($match[0]);
?>
Output:-
u2=HA853
How can i retrieve only HA853?
The 0 group is everything that the regex matched so either use \K to ignore the previous matches of the regex,
$subject="http://www.example.com?u1=US&u2=HA853&u3=HPA"; //my url
$pattern='/u2=\K[0-9A-Za-z]*/'; //R.E that url value is only digit/Alphabet
preg_match($pattern,$subject,$match);
print_r($match[0]);
or use a second capture group:
...
$pattern='/u2=([0-9A-Za-z]*)/'; //R.E that url value is only digit/Alphabet
...
print_r($match[1]);
Why you'd need to do that though is unclear to me, http://php.net/manual/en/function.parse-str.php, seems like a simpler approach.
$subject="http://www.example.com?u1=US&u2=HA853&u3=HPA";
parse_str($subject, $output);
echo $output['u2'];
Demo: https://3v4l.org/gR4cb
Other way is to use parse_url,http://php.net/manual/en/function.parse-url.php
$subject="http://www.example.com?u1=US&u2=HA853&u3=HPA";
$query_string = parse_url($subject, PHP_URL_QUERY); // get query string
$parameters = explode('&', $query_string); //Explode with &
$array = array(); // define an empty array
foreach($parameters as $val)
{
$param= explode('=', $val);
$array[$param[0]] = $param[1];
}
echo $array['u2']; // outputs HA853
print_r($array);
Array
(
[u1] => US
[u2] => HA853
[u3] => HPA
)

PHP - How to search an associative array by matching the key against a regexp

I am currently working on a small script to convert data coming from an external source. Depending on the content I need to map this data to something that makes sense to my application.
A sample input could be:
$input = 'We need to buy paper towels.'
Currently I have the following approach:
// Setup an assoc_array what regexp match should be mapped to which itemId
private $itemIdMap = [ '/paper\stowels/' => '3746473294' ];
// Match the $input ($key) against the $map and return the first match
private function getValueByRegexp($key, $map) {
$match = preg_grep($key, $map);
if (count($match) > 0) {
return $match[0];
} else {
return '';
}
}
This raises the following error on execution:
Warning: preg_grep(): Delimiter must not be alphanumeric or backslash
What am I doing wrong and how could this be solved?
In preg_grep manual order of arguments is:
string $pattern , array $input
In your code $match = preg_grep($key, $map); - $key is input string, $map is a pattern.
So, your call is
$match = preg_grep(
'We need to buy paper towels.',
[ '/paper\stowels/' => '3746473294' ]
);
So, do you really try to find string We need to buy paper towels in a number 3746473294?
So first fix can be - swap'em and cast second argument to array:
$match = preg_grep($map, array($key));
But here comes second error - $itemIdMap is array. You can't use array as regexp. Only scalar values (more strictly - strings) can be used. This leads you to:
$match = preg_grep($map['/paper\stowels/'], $key);
Which is definitely not what you want, right?
The solution:
$input = 'We need to buy paper towels.';
$itemIdMap = [
'/paper\stowels/' => '3746473294',
'/other\sstuff/' => '234432',
'/to\sbuy/' => '111222',
];
foreach ($itemIdMap as $k => $v) {
if (preg_match($k, $input)) {
echo $v . PHP_EOL;
}
}
Your wrong assumption is that you think you can find any item from array of regexps in a single string with preg_grep, but it's not right. Instead, preg_grep searches elements of array, which fit one single regexp. So, you just used the wrong function.

A bit lost with preg_match regular expression

I'm a beginner in regular expression so it didn't take long for me to get totally lost :]
What I need to do:
I've got a string of values 'a:b,a2:b2,a3:b3,a4:b4' where I need to search for a specific pair of values (ie: a2:b2) by the second value of the pair given (b2) and get the first value of the pair as an output (a2).
All characters are allowed (except ',' which seperates each pair of values) and any of the second values (b,b2,b3,b4) is unique (cant be present more than once in the string)
Let me show a better example as the previous may not be clear:
This is a string: 2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,never:0
Searched pattern is: 5
I thought, the best way was to use function called preg_match with subpattern feature.
So I tried the following:
$str = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
$re = '/(?P<name>\w+):5$/';
preg_match($re, $str, $matches);
echo $matches['name'];
Wanted output was '5 minutes' but it didn't work.
I would also like to stick with Perl-Compatible reg. expressions as the code above is included in a PHP script.
Can anyone help me out? I'm getting a little bit desperate now, as Ive spent on this most of the day by now ...
Thanks to all of you guys.
$str = '2 minutes:2,51 seconds:51,5 minutes:5,10 minutes:10,15 minutes:51,never:0';
$search = 5;
preg_match("~([^,\:]+?)\:".preg_quote($search)."(?:,|$)~", $str, $m);
echo '<pre>'; print_r($m); echo '</pre>';
Output:
Array
(
[0] => 5 minutes:5
[1] => 5 minutes
)
$re = '/(?:^|,)(?P<name>[^:]*):5(?:,|$)/';
Besides the problem of your expression having to match $ after 5, which would only work if 5 were the last element, you also want to make sure that after 5 either nothing comes or another pair comes; that before the first element of the pair comes either another element or the beginning of the string, and you want to match more than \w in the first element of the pair.
A preg_match call will be shorter for certain, but I think I wouldn't bother with regular expressions, and instead just use string and array manipulations.
$pairstring = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
function match_pair($searchval, $pairstring) {
$pairs = explode(",", $str);
foreach ($pairs as $pair) {
$each = explode(":", $pair);
if ($each[1] == $searchval) {
echo $each[0];
}
}
}
// Call as:
match_pair(5, $pairstring);
Almost the same as #Michael's. It doesn't search for an element but constructs an array of the string. You say that values are unique so they are used as keys in my array:
$str = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
$a = array();
foreach(explode(',', $str) as $elem){
list($key, $val) = explode(':', $elem);
$a[$val] = $key;
}
Then accessing an element is very simple:
echo $a[5];

preg_replace all characters up to a certain one

I have a string
&168491968426|mobile|3|100|1&185601651932|mobile|3|120|1&114192088691|mobile|3|555|5&
and i have to delete, say, this part &185601651932|mobile|3|120|1& (starting with amp and ending with amp) knowing only the first number up to vertical line (185601651932)
so that in result i would have
&168491968426|mobile|3|100|1&114192088691|mobile|3|555|5&
How could i do that with PHP preg_replace function. The number of line (|) separated values would be always the same, but still, id like to have a flexible pattern, not depending on the number of lines in between the & sign.
Thanks.
P.S. Also, I would be greatful for a link to a good simply written resource relating regular expressions in php. There are plenty of them in google :) but maybe you happen to have a really great link
preg_replace("/&185601651932\\|[^&]+&/", ...)
Generalized,
$i = 185601651932;
preg_replace("/&$i\\|[^&]+&/", ...);
if you want real flexibility, use preg_replace_callback. http://php.net/manual/en/function.preg-replace-callback.php
Important: don't forget to escape your number using preg_quote():
$string = '&168491968426|mobile|3|100|1&185601651932|mobile|3|120|1&114192088691|mobile|3|555|5&';
$number = 185601651932;
if (preg_match('/&' . preg_quote($number, '/') . '.*?&/', $string, $matches)) {
// $matches[0] contains the captured string
}
It seems to me you ought to be using another data structure than a string to manipulate this data.
I'd want this data in a structure like
Array(
[id] => Array(
[field_1] => value_1
[field_2] => value_2
)
)
Your massive string can be massaged into such a structure by doing something like this:
$data_str = '168491968426|mobile|3|100|1&185601651932|mobile|3|120|1&114192088691|mobile|3|555|5&';
$remove_num = '185601651932';
/* Enter a descriptive name for each of the numbers here
- these will be field names in the data structure */
$field_names = array(
'number',
'phone_type',
'some_num1',
'some_num2',
'some_num3'
);
/* split the string into its parts, and place them into the $data array */
$data = array();
$tmp = explode('&', trim($data_str, '&'));
foreach($tmp as $record) {
$fields = explode('|', trim($record, '|'));
$data[$fields[0]] = array_combine($field_names, $fields);
}
echo "<h2>Data structure:</h2><pre>"; print_r($data); echo "</pre>\n";
/* Now to remove our number */
unset($data[$remove_num]);
echo "<h2>Data after removal:</h2><pre>"; print_r($data); echo "</pre>\n";

Categories