Regex negative match in middle of string - php

I have these strings (checking every line separately):
adminUpdate
adminDeleteEx
adminEditXe
adminExclude
listWebsite
listEx
Now I want to match anything that starts with admin but does not ends with Ex (case-insensitive)
So after applying regex I must match:
adminUpdate
adminEditXe
adminExclude
My current regex is /^admin[a-z]+(?!ex)$/gi but it matches anything that starts with admin

Just a slight change:
/^admin[a-z]+(?<!ex)$/gi
^
Turn your look-ahead into a look-behind.
It is quite hard to explain in the current form. Basically, you need to reach the end of the string $ for the regex to match, and when it happens, (?!ex) is at the end of the string, so it can't see anything ahead. However, since we are at the end of the string, we can use a look-behind (?<!ex) to check whether the string ends with ex or not.
Since look-around is zero-width, we can swap the position of (?<!ex) and $ without changing the meaning (it does change how the engine searches for the matched string, though):
/^admin[a-z]+$(?<!ex)/gi
It is counter-intuitive to write it this way, but easier to see where my argument goes.
Another way to look at it is: due to the fact that (?!ex) and $ are zero-width assertion, they are checked at the same position, and being at the end of the string $ implies you won't see anything ahead.

^(?!.*Ex$)admin.*$
Try this.See demo.
https://regex101.com/r/sH8aR8/23
$re = ""^(?!.*Ex$)admin.*$"m";
$str = "adminUpdate\nadminDeleteEx\nadminEditXe\nadminExclude\nlistWebsite\nlistEx";
preg_match_all($re, $str, $matches);

Related

Regex - Match characters but don't include within results

I have got the following Regex, which ALMOST works...
(?:^https?:\/\/)(?:www|[a-z]+)\.([^.]+)
I need the result to be the only result, or within the same position in the Array.
So for example this http://m.facebook.com/ matches perfect, there is only 1 group.
However, if I change it to http://facebook.com/ then I get com/in place of where Facebook should be. So I need to have (?:www|[a-z]+) as an optional check really.
Edit:
What I expect is just to match facebook, if ANY of the strings are as follows:
http://www.facebook.com
http://facebook.com
http://m.facebook.com
And obviously the https counterparts.
This is my Regex now
(?:^https?:\/\/)(?:www)?\.?([^.]+)
This is close, however it matches the m on when I try `http://m.facebook.com
https://regex101.com/r/GDapY5/1
So I need to have (?:www|[a-z]+) as an optional check really.
A ? at the end of a pattern is generally used for "optional" bits -- it means "match zero or one" of that thing, so your subpattern would be something like this:
(?:www|[a-z]+)?
If you're simply trying to get the second level domain, I wouldn't bother with regex, because you'll be constantly adjusting it to handle special cases you come across. Just split on dots and take the penultimate value:
$domain = array_reverse(explode('.', parse_url($str)['host']))[1];
Or:
$domain = array_reverse(explode('.', parse_url($str, PHP_URL_HOST)))[1];
Perhaps you could make the first m. part optional with (?:\w+\.)?.
Instead of a capturing group you could use \K to reset the starting point of the reported match.
Then match one or more word characters \w+ and use a positive lookahead to assert that what follows is a dot (?=\.)
For example:
^https?://(?:www)?(?:\w+\.)?\K\w+(?=\.)
Edit: Or you could match for m. or www. using an alternation:
^https?://(?:m\.|www\.)?\K\w+(?=\.)
Demo Php

Match sub-strings that start with a character

I want to match parts of a string that start with a certain character (asterisk):
abc*DEFxyz => *DEF
abc*DE*Fxyz => *DE, *F
Tried preg_match_all('/[$\*A-Z]+/', $string, $matches); But it doesn't seem to work. I get *DE*F on the 2nd example
Change your regex to this :
\*[A-Z]+
http://regexr.com?34itc
Your regex here : [$\*A-Z]+ means a string containing * and A-Z characters, not mentioning anything about start.
Try:
^[^*]*\*
which says "from the start of the line, skip over all non-asterisk characters and stop at the first"
Extending this:
s/^[^*]*\*(.*)/
Will return the remainder of the string after the asterisk. To include the asterisk, adjust like this
s/^[^*]*(\*.*)/
Here's a great tool for checking your regex: http://gskinner.com/RegExr/
Hope this helps

How can I check if a string EXACTLY matches a regex pattern?

I'm working on a registration script for my client's product sales website.
I'm currently working on a reference ID input area, and I want to make sure that the reference ID is within the correct parameters of the payment method
The Reference ID will look something like this: XXXXX-XXXXX-XXXXX
I'm trying to use this RegEx pattern to match it: /(\w+){5}-(\w+){5}-(\w+){5}/
This matches it perfectly, but it also matches XXXXX-XXXXX-XXXXXXXXXX
Or at least it finds a match in there. I want it to make sure the entire string matches. I'm not too familiar with RegEx
How can I do this?
You need to use start and finish anchors. Alternatively, if you don't need to capture those groups, you can omit the parenthesis.
Also, the +{5} means match more than once exactly 5 times. I believe you didn't want that so I dropped the +.
/^\w{5}-\w{5}-\w{5}\z/
Also, I used \z so your string doesn't match "abcde-12345-edcba\n".
Use ^ and $ to match the start and end of the input string, respectively.
Also note that your use of + was superfluous, as (\w+){5} means "a word character, at least once, times five" which means it can match at least five times. You probably meant (\w){5} (or just \w{5} if you don't need the backreference; I'll assume in my example that you do).
/^(\w){5}-(\w){5}-(\w){5}$/
put the regular expression in between ^ and $ to match the whole string and check if it matches anything
example:
/^(\w+){5}-(\w+){5}-(\w+){5}$/
Try
/^([\w]{5,5})-([\w]{5,5})-([\w]{5,5})$/i
There are several online regex tester out there, I work with this one before I code.
Enclose it in "^" and "$" thus:
/^(\w+){5}-(\w+){5}-(\w+){5}$/
You need ^ to match the start of the string and $ to match the end:
/^\w{5}-\w{5}-\w{5}$/
Note that (\w+){5} is incorrect because that means five repetitions of \w+, but that in turn means "one or more word characters".
/^(\w){5}-(\w){5}-(\w){5}$/
You need to explicitly say that you want the pattern to start at the beginning of the string and end at it's ending.
You can improve it: /^((\w){5}-){2}(\w){5}$/ ; this way, you can easily modify the number of elements your serial number might have.
Use ^ and $ to mark the start and end of the regex string:
/^\w{5}-\w{5}-\w{5}$/
http://www.regular-expressions.info/anchors.html
In preg, \b marks word boundaries. So you could try with something like
/\b(\w+){5}-(\w+){5}-(\w+){5}\b/

Need to negate this regex pattern, but no clue how

I found a regex pattern for PHP that does the exact OPPOSITE of what I'm needing, and I'm wondering how I can reverse it?
Let's say I have the following text: Item_154 ($12)
This pattern /\((.*?)\)/ gets what's inside the parenthesis, but I need to get "Item_154" and cut out what's in parenthesis and the space before the parenthesis.
Anybody know how I can do that?
Regex is above my head apparently...
/^([^( ]*)/
Match everything from the start of the string until the first space or (.
If the item you need to match can have spaces in it, and you only want to get rid of whitespace immediately before the parenthetical, then you can use this instead:
/^([^(]*?)\s*\(/
The following will match anything that looks like text (...) but returns just the text part in the match.
\w+(?=\s*\([^)]*\))
Explanation:
The \w includes alphanumeric and underscore, with + saying match one or more.
The (?= ) group is positive lookahead, saying "confirm this exists but don't match it".
Then we have \s for whitespace, and * saying zero or more.
The \( and \) matches literal ( and ) characters (since its normally a special chat).
The [^)] is anything non-) character, and again * is zero or more.
Hopefully all makes sense?
/(.*)\(.*\)/
What is not in () will now be your 1st match :)
One site that really helped me was http://gskinner.com/RegExr/
It'll let you build a regex and then paste in some sample targets/text to test it against, highlighting matches. All of the possible regex components are listed on the right with (essentially) a tooltip describing the function.
<?php
$string = 'Item_154 ($12)';
$pattern = '/(.*)\(.*?\)/';
preg_match($pattern, $string, $matches);
var_dump($matches[1]);
?>
Should get you Item_154
The following regex works for your string as a replacement if that helps? :-
\s*\(.*?\)
Here's an explanation of what's it doing...
Whitespace, any number of repetitions - \s*
Literal - \(
Any character, any number of repetitions, as few as possible - .*?
Literal - \)
I've found Expresso (http://www.ultrapico.com/) is the best way of learning/working out regular expressions.
HTH
Here is a one-shot to do the whole thing
$text = 'Item_154 ($12)';
$text = preg_replace('/([^\s]*)\s(\()[^)]*(\))/', $1$2$3, $text);
var_dump($text);
//Outputs: Item_154()
Keep in mind that using any PCRE functions involves a fair amount of overhead, so if you are using something like this in a long loop and the text is simple, you could probably do something like this with substr/strpos and then concat the parens on to the end since you know that they should be empty anyway.
That said, if you are looking to learn REGEXs and be productive with them, I would suggest checking out: http://rexv.org
I've found the PCRE tool there to very useful, though it can be quirky in certain ways. In particular, any examples that you work with there should only use single quotes if possible, as it doesn't work with double quotes correctly.
Also, to really get a grip on how to use regexs, I would check out Mastering Regular Expressions by Jeffrey Friedl ISBN-13:978-0596528126
Since you are using PHP, I would try to get the 3rd Edition since it has a section specifically on PHP PCRE. Just make sure to read the first 6 chapters first since they give you the foundation needed to work with the material in that particular chapter. If you see the 2nd Edition on the cheap somewhere, that pretty much the same core material, so it would be a good buy as well.

php non-greedy regex problem

demo:
$str = 'bcs >Hello >If see below!';
$repstr = preg_replace('/>[A-Z0-9].*?see below[^,\.<]*/','',$str);
echo $repstr;
What I want this tiny programme to output is "bcs >Hello ",but in fact it's only "bcs "
What's wrong with my pattern?
I think the problem is that you're misinterpreting how a non-greedy quantifier acts. Once it's in operation, yes, it stops earlier than it would otherwise. But it isn't aware of what comes before it (or potentially the text that comes later, either). It's only concerned with it's current position. Hence, the regular expression you posted will match all of:
">Hello >If see below!"
Let's see how this works:
/>[A-Z0-9].*?see below[^,\.<]*/
The regex first looks for ">" in "bcs >Hello >If see below!", and finds the first one, which is the one right before "Hello". Ok, let's check the next part of the expression:
[A-Z0-9]
The next char is a H, which matches the pattern [A-Z0-9]. Still good! Next:
.*?
Now we match all non-newline chars until we get to the first instance to match the remaining expressions of "see below[^,.<]*". If we had used just a plain greedy quantifier, we could match through multiple cases of "see below[^,.<]*" until we matched the last possible one. (So if your string had continued on, and there'd been other text match that pattern, it would have captured that as well) The non-greedy quantifier doesn't mean that your whole pattern will return the smallest possible match of all possible matches in the string. It just dictates how that particular character match functions.
You might want to try the following pattern then:
/>[A-Z0-9][^>]*?see below[^,\.<]*/
Hopefully this clears it up!
Why don't you write it like this:
$str = 'bcs >Hello >If see below!';
$repstr = preg_replace('/>If see below[^,\.<]*/','',$str);
echo $repstr;
This might be a good alternative to what you have.
The problem with your regexp is that instead of selecting what you want, you are selecting what you don't want and replacing that with an empty string.
The best approach, in my opinion, is selecting what you want, that is what the code below does. What you end up with is what is what is matched by the first sub-pattern otherwise you get your string back.
$str = 'bcs >Hello >If see below!';
$repstr = preg_replace('/^([\w]+ >[\w]+).*?see below.*?$/i', '$1', $str);
var_dump($repstr);
I hope this helps.

Categories