PHP- Regular expression - how to read from Right to left - php

I have below example
$game = "hello999hello888hello777last";
preg_match('/hello(.*?)last/', $game, $match);
The above code returns 999hello888hello777, what I need is to retrieve the value just before Last, i.e 777. So I need to read regular expression to read from right to left.

$game = strrev($game);
How about that? :D
Then just reverse the regular expression ^__^

Why not just reverse the string? Use PHP's strrev and then just reverse your regular expression.
$game = "hello999hello888hello777last";
preg_match('/tsal(.*?)elloh/', strrev($game), $match);

This will return the last set of digits before the string last
$game = "hello999hello888hello777last";
preg_match('/hello(\d+)last$/', $game, $match);
print_r($match);
Output Example:
Array
(
[0] => hello777last
[1] => 777
)
So you would need $match[1]; for the 777 value

Your problem is that although .* matches reluctantly, i. e. as few characters as possible, it still starts matching right after hello, and since it matches any characters, it will match right across "boundaries" (last and hello in your case).
Therefore you need to be more explicit about the fact that it's not legal to match across boundaries, and that's what lookahead assertions are for:
preg_match('/hello((?:(?!hello|last).)*)last(?!.*(?:hello|last)/', $game, $match);
Now the match between hello and last is prohibited from containing hello and/or last, and it's not allowed to have hello or last after the match.

Related

How do I match this pattern using preg_match in PHP?

I'm writing a simple quiz engine in PHP and supply the question text in this format
question|correct/feedback|wrong/feedback|wrong/feedback
There can be as many wrong/feedback options as necessary. I want to use preg_match to return the results so I can display them. For instance:
q|aaa/aaa|bbb/bbb|ccc/ccc
...should return...
array(
0 => q|aaa/aaa|bbb/bbb|ccc/ccc
1 => q
2 => aaa/aaa
3 => bbb/bbb
4 => ccc/ccc
)
So, far I've got this regular expression which matches the question and the correct/feedback combination...
([^\|]+)\|([^\/]+\/[^\|$]+)
...but I have no idea how to match the remaining wrong/feedback strings
You can also use the "glue" feature in your pattern with preg_match_all, this way it's possible to check if the syntax is correct and to extract each part at the same time.
The glue feature ensures that each match follows immediately the previous match without gap. To do that I use the A global modifier (Anchored to the start of the string or the next position after the previous match).
$s = 'q|aaa/aaa|bbb/bbb|ccc/ccc';
$pat = '~ (?!\A) \| \K [^|/]+ / [^|/]+ (?: \z (*:END) )? | \A [^|/]+ ~Ax';
if ( preg_match_all($pat, $s, $m) && isset($m['MARK']) ) {
$result = $m[0];
print_r($result);
}
I use also a marker (*:END) to be sure that the end of the string is well reached despite of the pattern constraints. If this marker exists in the matches array, it's a proof that the syntax is correct. Advantage: you have to parse the string only once (you don't even need to check the whole string syntax in a lookahead assertion anchored at the start of the string).
demo
If you want the whole question as first item in the result array, just write:
$result = array_merge([$s], $m[0]);
So, after the advice, I've decided to use preg_match to check the syntax and then explode to split the string.
This regex seems to match the string format up until any mismatch occurs.
^[^\|/]+(?:\|[^\|/]+/[^\|/]+)+
If I check that the length of the match is the same as the original string I think this will tell me the syntax is correct. Does this sound feasible?

PHP preg_match returns only first match

The first question is this:
I am using http://www.phpliveregex.com/ to check my regex is right and it finds more than one matching lines.
I am doing this regex:
$lines = explode('\n', $text);
foreach($lines as $line) {
$matches = [];
preg_match("/[0-9]+[A-Z][a-z]+ [A-Z][a-z]+S[0-9]+\-[0-9]+T[0-9]+/uim", $line, $matches);
print_r($matches);
}
on the $text which looks like this: http://pastebin.com/9UQ5wNRu
The problem is that printed matches is only one match:
Array
(
[0] => 3Bajus StanislavS2415079249-2615T01
)
Why is it doing to me? any ideas what could fix the problem?
The second question
Maybe you've noticed not regular alphabetic characters of slovak language inside the text (from pastebin). How to match those characters and select the users which have this format:
{number}{first_name}{space}{last_name}{id_number}
how to do that?
Ok first issue is fixed. Thank you #chris85 . I should have used preg_match_all and do it on the whole text. Now I get an array of all students which have non-slovak (english) letters in the name.
preg_match is for one match. You need to use preg_match_all for a global search.
[A-Z] does not include an characters outside that range. Since you are using the i modifier that character class actual is [A-Za-z] which may or may not be what you want. You can use \p{L} in place of that for characters from any language.
Demo: https://regex101.com/r/L5g3C9/1
So your PHP code just be:
preg_match_all("/^[0-9]+\p{L}+ \p{L}+S[0-9]+\-[0-9]+T[0-9]+$/uim", $text, $matches);
print_r($matches);
You can also use T-Regx library:
pattern("^[0-9]+\p{L}+ \p{L}+S[0-9]+\-[0-9]+T[0-9]+$", 'uim')->match($text)->all();

Move multiple letters in string using regex

Using a regular expression I want to move two letters in a string.
W28
L36
W29-L32
Should be changed to:
28W
36L
29W-32L
The numbers vary between 25 and 44. The letters that need to be moved are always "W" and/or "L" and the "W" is always first when they both exist in the string.
I need to do this with a single regular expression using PHP. Any ideas would be awesome!
EDIT:
I'm new to regular expressions and tried a lot of things without success. The closest I came was using "/\b(W34)\b/" for each possibility. I also found something about using variables in the replace function but had no luck using these.
Your regex \b(W34)\b matches exactly W34 as a whole word. You need a character class to match W or L, and some alternatives to match the numeric range, and use the most of capturing groups.
You can use the following regex replacement:
$re = '/\b([WL])(2[5-9]|3[0-9]|4[0-4])\b/';
$str = "W28\nL36\nW29-L32";
$result = preg_replace($re, "$2$1", $str);
echo $result;
See IDEONE demo
Here, ([WL]) matches and captures either W or L into group 1, and (2[5-9]|3[0-9]|4[0-4]) matches integer numbers from 25 till 44 and captures into group 2. Backreferences are used to reverse the order of the groups in the replacement string.
And here is a regex demo in case you want to adjust it later.

preg_replace with Regex - find number-sequence in URL

I'm a regex-noobie, so sorry for this "simple" question:
I've got an URL like following:
http://stellenanzeige.monster.de/COST-ENGINEER-AUTOMOTIVE-m-w-Job-Mainz-Rheinland-Pfalz-Deutschland-146370543.aspx
what I'm going to archieve is getting the number-sequence (aka Job-ID) right before the ".aspx" with preg_replace.
I've already figured out that the regex for finding it could be
(?!.*-).*(?=\.)
Now preg_replace needs the opposite of that regular expression. How can I archieve that? Also worth mentioning:
The URL can have multiple numbers in it. I only need the sequence right before ".aspx". Also, there could be some php attributes behind the ".aspx" like "&mobile=true"
Thank you for your answers!
You can use:
$re = '/[^-.]+(?=\.aspx)/i';
preg_match($re, $input, $matches);
//=> 146370543
This will match text not a hyphen and not a dot and that is followed by .aspx using a lookahead (?=\.aspx).
RegEx Demo
You can just use preg_match (you don't need preg_replace, as you don't want to change the original string) and capture the number before the .aspx, which is always at the end, so the simplest way, I could think of is:
<?php
$string = "http://stellenanzeige.monster.de/COST-ENGINEER-AUTOMOTIVE-m-w-Job-Mainz-Rheinland-Pfalz-Deutschland-146370543.aspx";
$regex = '/([0-9]+)\.aspx$/';
preg_match($regex, $string, $results);
print $results[1];
?>
A short explanation:
$result contains an array of results; as the whole string, that is searched for is the complete regex, the first element contains this match, so it would be 146370543.aspx in this example. The second element contains the group captured by using the parentheeses around [0-9]+.
You can get the opposite by using this regex:
(\D*)\d+(.*)
Working demo
MATCH 1
1. [0-100] `http://stellenanzeige.monster.de/COST-ENGINEER-AUTOMOTIVE-m-w-Job-Mainz-Rheinland-Pfalz-Deutschland-`
2. [109-114] `.aspx`
Even if you just want the number for that url you can use this regex:
(\d+)

pregmatch between characters and any numeric

I'm stuck writing a preg_match
I have a string:
XPMG_ar121023.txt
and need to extract the 2 letters between XPMG_ and the first digit - be it a 0-9
$str = 'XPMG_ar121023.txt';
preg_match('/('XPMG_')|[0-9\,]))/', $str, $match);
print_r($match);
Maybe this isn't the best option: My characters will always be
You can just do
$str = "XPMG_ar121023.txt" ;
preg_match('/_([a-z]+)/i', $str, $match);
var_dump($match[1]);
Output
string 'ar' (length=2)
This is too simple for a regular expression. Just $match = substr($str,5,3) would get what you're asking for.
Let me walk through this step by step so as to help you solve similar problems in the future. Suppose we have the following format for our filenames:
XPMG_ar121023.txt
We know what we want to capture, we want the "ar" right after the _ and just before the numbers begin. So our expression would look something like this:
_[a-z]+
This is pretty straight-forward. We're starting by looking for an underscore, followed by any number of letters between a and z. The square brackets define a character class. Our class consists of the alphabet, but you can push specific numbers in there and more if you like.
Now because we want to capture only the letters, we need to put parenthesis around that part of the pattern:
_([a-z]+)
In the result we will now have access to only that subpattern. Next we put our delimiters in place to specify where our pattern begins, and ends:
/_([a-z]+)/
And lastly, after our closing delimiter we can add some modifiers. As it is written, our pattern only looks for lower-case letters. We can add the i modifier to make this case-insensitive:
/_([a-z]+)/i
Voila, we're done. Now we can pass it into preg_match to see what it spits out:
preg_match( "/_([a-z]+)/i", "XPMG_ar121023.txt", $match );
This function takes a pattern as the first parameter, a string to match it against as the second, and lastly a variable to spit the results into. When all is said and done, we can check $match for our data.
The results of this operation follow:
array(2) {
[0]=> string(3) "_ar"
[1]=> string(2) "ar"
}
This is the contents of $match. Notice our full pattern is found in the first index of the array, and our captured portion is provided in the second index of the array.
echo $match[1]; // ar
Hope this helps.
Well, why not:
$letters = $str[5].$str[6];
:)
After all, you'll always need the 2 chars after the fixed prefix, there are many ways that do not require a regexp (substr() being the best anyway)

Categories