Re-order regular expression matches - php

Is there a way to get the match patterns to change order? For example if you have a string with letters-digits and using preg_match_all(), and you want the resulting match array to have the digits before the letters. Is there a way to specify this in the regular expression itself?
So "aaa-111" would result in matches with
array(0 => '111', 1 => 'aaa');

Perhaps named capture groups will help. Example:
preg_match('/(?<alphapart>[a-z]+)-(?<numpart>[0-9]+)/', 'aaa-111', $matches);
$matches:
array('alphapart' => 'asd', 'numpart' => '111')
This way you can refer to the matches by a name instead of whatever order index they were matched in.
Edit: Just for accuracy, I want to note that $matches will actually include the matches by index as well, so the actual $matches will be: array(5) { [0]=> string(7) "aaa-111" ["alphapart"]=> string(3) "aaa" [1]=> string(3) "aaa" ["numpart"]=> string(3) "111" [2]=> string(3) "111" }

The order of groups in a regex is dependent on their positions in the regex and the string. Changing the order would make it very confusing.
What you can do is use "named groups".
/(?P<letters>\w*)-(?P<digits>\d*)/
The array will still be in the same order, but, you can use $matches['digits'] to easily get just the digits.
DEMO: http://ideone.com/3tRJLZ

Yes you can. You can use lookaheads that don't push the 'cursor' and so you could first match the last part, and then the first part. It works with (?=regex)
This works:
(?=\w+\-(\d+))(\w+)\-\d+
but will also give the full match at index 0. Like ["aaa-111", "111", "aaa"]
is that a problem?

I don't believe there is. Regex isn't designed to sort. You could setup two different regular expressions to check for each pattern though. This code will echo the two string in num/alpha order as you requested:
<?php
header('Content-Type: text/plain');
$string1 = 'aaa-123';
$string2 = '123-aaa';
echo 'String 1: '.$string1."\n";
echo 'String 2: '.$string2."\n";
$pattern1 = '/([\d]+)-([a-z]+)/i';
$pattern2 = '/([a-z]+)-([\d]+)/i';
echo 'Result 1: ';
if(preg_match($pattern1, $string1, $matches))
{
echo $matches[1].' '.$matches[2]."\n";
}
if(preg_match($pattern2, $string1, $matches))
{
echo $matches[2].' '.$matches[1]."\n";
}
echo 'Result 2: ';
if(preg_match($pattern1, $string2, $matches))
{
echo $matches[1].' '.$matches[2]."\n";
}
if(preg_match($pattern2, $string2, $matches))
{
echo $matches[2].' '.$matches[1]."\n";
}
?>
The resulting output is:
String 1: aaa-123
String 2: 123-aaa
Result 1: 123 aaa
Result 2: 123 aaa

If you want the digits to be there first, you need to sort the array yourself.
array_sort() will... sort it out for you.

Related

How to write a regex pattern which matches following Array?

I want to write a regex pattern that user enters values matches the any one of the index in the following array ,can you please help me to achieve this thing...
$cars=['bmw','Audi','Ferari','BenZ',];
$regex_pattern=^[A-Za-z]+(?: [A-Za-z]+)*$; //it takes the value only string with space or without space,now i want to match the pattern with the given array values .
if(preg_match($regex_pattern,$request->search)){
my logic
}
InvalidScenarios:
Flight
Ship
Valid-Scenarios:
bmw
Audi
Ferari
Benz
$cars=['bmw','Audi','Ferari','BenZ',];
$regex_pattern=^[A-Za-z ]+$; //it takes the value only string with space or without space,now i want to match the pattern with the given array values .
if(preg_match($regex_pattern,$request->search)){
my logic
}
you can use
$matches = preg_grep ('/^ship (\w+)/i', $cars);
Output : []
$matches = preg_grep ('/^bmw (\w+)/i', $cars);
Output : [ 0 => 'bmw' ];
It will return array of values matched.
You can check for $matches if it is an empty array that means you don't have any match.
Reference : https://www.php.net/manual/en/function.preg-grep.php
You could write a dynamic pattern to assert for the word in the string, and then use your regex to match only the allowed characters.
$cars=['bmw','Audi','Ferari','BenZ'];
$str = 'bmw
Audi
Ferari
Benz
Flight
Ship';
$regex_pattern = sprintf
("/^(?=.*\b(?:%s)\b)[A-Za-z]+(?: [A-Za-z]+)*$/im",
implode('|', $cars)
);
preg_match_all($regex_pattern, $str, $matches);
var_dump($matches[0]);
Output
array(4) {
[0]=>
string(3) "bmw"
[1]=>
string(4) "Audi"
[2]=>
string(6) "Ferari"
[3]=>
string(4) "Benz"
}

preg_match_all : Assign same name to two subpatterns

I want to use a regex to match two different subpatterns and give them the same name using the PCRE_INFO_JCHANGED modifier (?J)
The two subpatterns are very different from each other so I have to catch them using |
What I usually do is give the two patterns a different name and then choose the one I want using PHP, but I'd like to know if it possible without PHP
Example here : https://3v4l.org/GEMeT (edited thanks to #JustOnUnderMillions)
The 2nd ?P<number> will always capture and replace the first ?P<number>
What I want : Capture both patterns with one regex and store them both with the same key number
Desired output :
Pattern 1
string(1) "1"
Pattern 2
string(1) "2"
Thanks for your help !
Dont use preg_match_all here
$regex = '/(?J)I wanna match pattern (?P<number>1) which is very different from pattern 2|(?P<number>2), again nothing to do with pattern 1 here/';
Result with preg_match:
array(3) {
[0]=>
string(62) "I wanna match pattern 1 which is very different from pattern 2"
["number"]=>
string(1) "1"
[1]=>
string(1) "1"
}
Full with fixed regex 'nothing similar' was not found in the orignal regex:
$text1 = 'I wanna match pattern 1 which is very different from pattern 2';
$text2 = 'I wanna match pattern 2, again nothing similar with pattern 1 here';
$regex = '/(?J)(I wanna match pattern (?P<number>1) which is very different from pattern 2|I wanna match pattern (?P<number>2), again nothing similar with pattern 1 here)/';
echo "Pattern 1\n";
preg_match( $regex, $text1, $matches );
var_dump($matches);
echo "\n\nPattern 2\n";
preg_match( $regex, $text2, $matches );
var_dump($matches);

Regular expression group. Extract as few parameters in one group

I have string some-other/other/ram/1gb/2gb/other, and want to parse ram group, which must capture repeatable parameters. (it's for url filtration ram/1gb/2gb/4gb, means group ram and captured values array(1gb, 2gb, 4gb) )
What I must change in my regular expression to capture array of parameters under one parameter group?
Now I have:
$str = "some-other/other/ram/1gb/2gb/other";
$possible_values = implode('|', array('1gb','2gb','4gb','8gb','12gb','16gb','32gb'))
$compiled = "#(.*)ram/(?P<ram>{$possible_values})(.*)$#uD";
if(preg_match($compiled,$str,$matches){
var_dump($matches['ram'])
//output is string '1gb', but I want to see array('1gb', '2gb')
}
what must I change? Thanks!
Try:
$compiled = "#^(.*)ram/(?P<ram>(?:{$possible_values}/?)+)(/(?:.*))$#uD";
The inner group matches any sequence of the possible values, separated /.
preg_match won't return an array for a repetition operator. Use explode('/', $matches['ram']) to split it up.
I guess this is probably easier than you think, here's my 5 cents:
$ram = "some-other/other/ram/1gb/2gb/other";
if (preg_match('%/ram/%i', $ram)) {
preg_match_all('/(\d+gb)/i', $ram, $matches, PREG_PATTERN_ORDER);
}
var_dump($matches[1]);
OUTPUT:
array(2) {
[0]=>
string(3) "1gb"
[1]=>
string(3) "2gb"
}
DEMO:
http://ideone.com/jMoobu

Stuck with PHP regex

I have big string in that I need to check if number is present which is more than 3.
Means "some string2" will be invalid , but "some string 3","some string7" will be correct.
preg_match('/some\s*string\s*([3-9][0-9]*|[1-9][0-9]+)/i', $haystack);
And here the working example
But, after examining your use-case, which seems to be checking for a specific version in an application description, I too would advise you to just get the number out of the string and compare it to an actual number to be sure it's larger or equal than 3:
preg_match('/([0-9]+)/', $string, $matches);
if ($matches[1] >= 3) {
// Do something
}
Regex is for text matching, not arithmetic. Right tool for the right job...
preg_match('/([0-9]+)/', $string, $matches);
if ($matches[1] >= 3) {
// Do something
}
You match a word followed by an optional space and then the number greater than 2. Thanks to the decimal places you can control that:
(\w*\s*(?:[1-9]\d+|[3-9]))
Some little example (demo):
$subject = 'I have big string in that I need to check if number is present which is more than 3.
Means "some string2" will be invalid , but "some string 3","some string7" will be correct.';
$pattern = '(\w*\s*(?:[1-9]\d+|[3-9]))';
$r = preg_match_all($pattern, $subject, $matches);
var_dump($matches);
Output:
array(1) {
[0]=>
array(3) {
[0]=>
string(6) "than 3"
[1]=>
string(8) "string 3"
[2]=>
string(7) "string7"
}
}
I hope this is helpful.
I modified Florian's solution:
[a-z]+\s?[a-z]+\s?([1-9][0-9]+|[3-9])
http://regexr.com?31ja1
It works for any string and not just "some string" and it allows only 0 or 1 whitespace character.
This wouldn't work?
$numberBiggerThanThree = preg_match('/([0-9]{2,}|[3-9])/', 'some long string 3');

How preg_match_all() processes strings?

I'm still learning a lot about PHP and string alteration is something that is of interest to me. I've used preg_match before for things like validating an email address or just searching for inquiries.
I just came from this post What's wrong in my regular expression? and was curious as to why the preg_match_all function produces 2 strings, 1 w/ some of the characters stripped and then the other w/ the desired output.
From what I understand about the function is that it goes over the string character by character using the RegEx to evaluate what to do with it. Could this RegEx have been structured in such a way as to bypass the first array entry and just produce the desired result?
and so you don't have to go to the other thread
$str = 'text^name1^Jony~text^secondname1^Smith~text^email1^example-
free#wpdevelop.com~';
preg_match_all('/\^([^^]*?)\~/', $str, $newStr);
for($i=0;$i<count($newStr[0]);$i++)
{
echo $newStr[0][$i].'<br>';
}
echo '<br><br><br>';
for($i=0;$i<count($newStr[1]);$i++)
{
echo $newStr[1][$i].'<br>';
}
This will output
^Jony~^Smith~^example-free#wpdevelop.com~JonySmithexample-free#wpdevelop.com
I'm curious if the reason for 2 array entries was due to the original sytax of the string or if it is the normal processing response of the function. Sorry if this shouldn't be here, but I'm really curious as to how this works.
thanks,
Brodie
It's standard behavior for preg_match and preg_match_all - the first string in the "matched values" array is the FULL string that was caught by the regex pattern. The subsequent array values are the 'capture groups', whose existence depends on the placement/position of () pairs in the regex pattern.
In your regex's case, /\^([^^]*?)\~/, the full matching string would be
^ Jony ~
| | |
^ ([^^]*?) ~ -> $newstr[0] = ^Jony~
-> $newstr[1] = Jony (due to the `()` capture group).
Could this RegEx have been structured in such a way as to bypass the first array entry and just produce the desired result?
Absolutely. Use assertions. This regex:
preg_match_all('/(?<=\^)[^^]*?(?=~)/', $str, $newStr);
Results in:
Array
(
[0] => Array
(
[0] => Jony
[1] => Smith
[2] => example-free#wpdevelop.com
)
)
As the manual states, this is the expected result (for the default PREG_PATTERN_ORDER flag). The first entry of $newStr contains all full pattern matches, the next result all matches for the first subpattern (in parentheses) and so on.
The first array in the result of preg_match_all returns the strings that match the whole pattern you passed to the preg_match_all() function, in your case /\^([^^]*?)\~/. Subsequent arrays in the result contain the matches for the parentheses in your pattern. Maybe it is easier to understand with an example:
$string = 'abcdefg';
preg_match_all('/ab(cd)e(fg)/', $string, $matches);
The $matches array will be
array(3) {
[0]=>
array(1) {
[0]=>
string(7) "abcdefg"
}
[1]=>
array(1) {
[0]=>
string(2) "cd"
}
[2]=>
array(1) {
[0]=>
string(2) "fg"
}
}
The first array will contain the match of the entire pattern, in this case 'abcdefg'. The second array will contain the match for the first set of parentheses, in this case 'cd'. The third array will contain the match for the second set of parentheses, in this case 'fg'.
[0] contains entire match, while [1] only a portion (the part you want to extract)...
You can do var_dump($newStr) to see the array structure, you'll figure it out.
$str = 'text^name1^Jony~text^secondname1^Smith~text^email1^example-
free#wpdevelop.com~';
preg_match_all('/\^([^^]*?)\~/', $str, $newStr);
$newStr = $newStr[1];
foreach($newStr as $key => $value)
{
echo $value."\n";
}
This will result in... (weird result, haven't modified expression)
Jony
Smith
example-
free#wpdevelop.com
Whenever you have problems to imagine the function of preg_match_all you should use an evaluator like preg_match_all tester # regextester.net
This shows you the result in realtime and you can configure things like the result order, meta instructions, offset capturing and many more.

Categories