I need 2 groups captured: 1-expr (can be empty); 2-essi
see code below
$s = 'regular expr<span>essi</span>on contains';
function my_func($matches){
//I need 2 groups captured
//$matches[1] - "expr" (see $s before span) - can be empty, but I still need to capture it
//$matches[2] - "essi" (between spans)
}
$pattern = "???";
echo preg_replace_callback($pattern, my_func, $s);
$pattern = "~(\w*)<span>(\w+)</span>~";
This should do the trick.
If the second group should be able to match empty strings as well, replace the + by another *. Note that \w will match letters, digits and underscores. If that is too much or insufficient, replace it by an appropriate character class.
One more thing: I think the syntax for preg_replace_callback requires you to hand in the function name as a string.
Related
The Context
I'm in need of a bit of code that takes a very simple math string and runs PHP's eval() function. For example ...
$math = '25 * (233 - 1.5)';
echo eval("return $math;"); // returns 5787.5
However eval() is quite dangerous in the wrong hands, so the variable must be scrubbed. For the above, for example, a simple preg_replace would be ...
$math = '25 * (233 - 1.5)';
$replace = '/[^0-9\(\)\.\,\+\-\*\/\s]/';
$math = preg_replace($replace, '', $math);
echo eval("return $math;"); // returns 5787.5
... which ensures $math only contains valid characters ... .,+-*/, spaces and numbers, and no malicious code.
The Question
I want to allow a few very specific words (PHP math functions), such as pow, pi, min, max, etc.
What's the cleanest way to validate both characters and words in regex?
So if given this string ...
pow(25,2) / pi(); hack the pentagon;
... how would I remove everything that wasn't in the $replace regex, but preserve the words pow and pi?
Using php, you can match those words that you don't want to remove and use a (*SKIP)(*FAIL) approach.
You can also shorten the character class by remove the backslashes, and if you use a different delimiter than / in php you also don't have to escape the /
As you are replacing the matched characters in the character class with an empty string, you can use a quantifier + to match 1 or more consecutive matches and do a single replacement.
\b(?:p(?:i|ow)|m(?:in|ax))\b(*SKIP)(*FAIL)|[^0-9().,+*/\s-]+
The pattern matches
\b(?:p(?:i|ow)|m(?:in|ax))\b Match either pi pow min or max
(*SKIP)(*FAIL)| What is matches so far should not be part of the match result
[^0-9().,+*/\s-]+ Match 1+ times any char except the listed chars in the negated character class
Regex demo
If you don't want the spaces at the start and end, you could consider to trim $math
$math = 'pow(25,2) / pi(); hack the pentagon;';
$replace = '~\b(?:p(?:i|ow)|m(?:in|ax))\b(*SKIP)(*FAIL)|[^0-9().,+*/\s-]+~';
$math = preg_replace($replace, '', $math);
echo eval("return $math;"); // returns 198.94367886487
I have a value like this 73b6424b. I want to split value into two parts. Like 73b6 and 424b. Then the two split value want to reverse. Like 424b and 73b6. And concatenate this two value like this 424b73b6. I have already done this like way
$substr_device_value = 73b6424b;
$first_value = substr($substr_device_value,0,4);
$second_value = substr($substr_device_value,4,8);
$final_value = $second_value.$first_value;
I am searching more than easy way what I have done. Is it possible?? If yes then approach please
You may use
preg_replace('~^(.{4})(.{4})$~', '$2$1', $s)
See the regex demo
Details
^ - matches the string start position
(.{4}) - captures any 4 chars into Group 1 ($1)
(.{4}) - captures any 4 chars into Group 2 ($2)
$ - end of string.
The '$2$1' replacement pattern swaps the values.
NOTE: If you want to pre-validate the data before swapping, you may replace . pattern with a more specific one, say, \w to only match word chars, or [[:alnum:]] to only match alphanumeric chars, or [0-9a-z] if you plan to only match strings containing digits and lowercase ASCII letters.
$txt = "toto1 555.4545.555.999.7465.432.674";
$rgx = "/([\w]+)\s([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/";
preg_match($rgx, $txt, $res);
var_dump($res);
I would like to simplify this pattern by avoiding repeating "([0-9]+)" because i don't know how many they are.
Any one can say me how ?
Here is a direct answer to the question, as you have stated it:
/[\w]+\s[0-9]+(?:\.[0-9]+)+/
However, note that I have removed all of the numbered capture groups. This could be problematic, depending on what you're actually trying to achieve.
It is not possible to "count" with capture groups in regular expressions, so you would need to write some other code (i.e. not just one match, with one regex, and using back-references) to deal with this if you wish to run any queries like "What digits appear after the fifth "."?"
There are two ways you can do this. If you just need to verify that the string matches the pattern, this regex will do the job: \w+\s(?:[0-9]+\.?)+
However, if you need to split the string in to it's component parts (in my interpretation, the beginning word followed by the sequence of decimal separated numbers), then you could use this pattern: (\w+)\s((?:[0-9]+\.?)+)
The second pattern will return the beginning word, toto1 in group 1, followed by the decimal separated numbers in group 2 555.4545.555.999.7465.432.674 which you could then split in PHP if required: $sequence = explode('.', $matches[2]);
What you need can be obtained with a preg_split with a regex matching 1 or more whitespaces or dots:
$txt = "toto1 555.4545.555.999.7465.432.674";
$rgx = '/[\s.]+/';
$res = preg_split($rgx, $txt);
print_r($res);
See the PHP demo
If you need a regex approach, you can use a \G based regex with preg_match_all:
'~(?|([\w]+)|(?!\A)\G[\s.]*([0-9]+))~'
See the regex demo and a PHP demo:
$txt = "toto1 555.4545.555.999.7465.432.674";
$rgx = '~(?|(\w+)|(?!\A)\G[\s.]*([0-9]+))~';
preg_match_all($rgx, $txt, $res);
print_r($res[1]);
Pattern details:
The (?|...) is a branch reset group to reset group IDs in all the branches
(\w+) - Group 1 matches 1+ word chars
| - or (then goes Branch 2)
(?!\A)\G - the end of the previous successful match
[\s.]* - zero or more whitespaces or dots
([0-9]+) - Group 1 (again!) matching 1 or more digits.
I'm creating in PHP a $pattern for a preg_match($pattern, $password) function, and it should be a regexp that defines a string made this way:
at least $str_lenght long AND
that has at least $num_capitals capital letters AND
that has at least $num_numerals numbers AND
that has at least $num_symbols from this range: !##$%^&*()-+?
How can I do it?
You can build your regex this way using lookaheads:
$re = '/(?=(.*?[A-Z]){' . $num_capitals . '})(?=(.*?[0-9]){' . $num_numerals .
'})(?=(.*?[!##$%^&*()+?-]){' . $num_symbols . '}).{' . $str_lenght . ',}/';
#anubhava greatly destroyed my answer, but I'll leave it for an alternative approach
. matches all characters, {5,} repeats it 5+ times. However, since we aren't making one long expression, I would still use the faster strlen(). Demo:
.{5,}
For the rest, I would match a character class and use preg_match_all() which will return the total number of matches (may be 0).
Here are the 3 character classes you want:
[A-Z]
[0-9] OR \d (\d will match other numeric characters, like Arabic, etc.)
[!##$%^&*()+?-]
An example implementation:
$count = preg_match_all('/[A-Z]/', 'FOObar', $matches);
// $count = 3;
Please note in the final character class ([!##$%^&*()+?-]), ^ must not come first and - must not be in the middle..otherwise you'll need to escape them with \ because they have special meanings.
Try this
$strRegex = '/((?=.*\d).{'.$num_numerals.'})((?=.*[a-z]).{'.$num_lower.'})((?=.*[A-Z]).{'$num_upper'})((?=.*[##$%]).{'.$num_symbols.'})/';
Or
$strRegex = '/((?=.*\d).{'.$num_numerals.'})((?=.*[a-z]).{1,'.$num_lower.'})((?=.*[A-Z]).{'$num_upper'})((?=.*[##$%]).{'.$num_symbols.'})/';
And based on $num_lower you can limit nuber of characters allowed in the password.
And the pass will accept lower case from 1 to $num_lower
I have variable strings like the below:
The.Test.String.A01Y18.123h.WIB-DI.DO5.1.K.314-ECO
The.Regex.F05P78.123h.WIB-DI.DO5.1.K.314-EYT
Word.C05F78.342T.DSW-RF.EF5.2.F.342-DDF
I would like to extract this part of these string in PHP dynamically and i was looking at using regex but haven't had much success:
The.Test.String.A01Y18
The.Regex.F05P78
Word.C05F78
And ultimately to:
The Test String A01Y18
The Regex F05P78
Word C05F78
The first part of the text will be variable in length and will separate each word with a period. The next part will always be the same length with the pattern:
One letter, 2 number, one letter, 2 numbers pattern (C05F78)
Any thing in the string after that is what I would like to remove.
that's it
$x=array(
"The.Test.String.A01Y18.123h.WIB-DI.DO5.1.K.314-ECO",
"The.Regex.F05P78.123h.WIB-DI.DO5.1.K.314-EYT",
"Word.C05F78.342T.DSW-RF.EF5.2.F.342-DDF"
);
for ($i=0, $tmp_count=count($x); $i<$tmp_count; ++$i) {
echo str_replace(".", " ", preg_replace("/^(.+?)([a-z]{1}[0-9]{2}[a-z]{1}[0-9]{2})\..+$/i", "\\1\\2", $x[$i]))."<br />";
}
Using this regular expression should work, replacing each of your strings with the first capturing group:
^((?:\w+\.)+\w\d{2}\w\d{2}).*
See demo at http://regex101.com/r/fR3pM6
This is valid too:
preg_match("\.*[\w\d]{6}", stringVariable)
.* for all digits atleast we found a composition of letters and words of 6 characters ([\w\d]{6})
Result:
Match 1: The.Test.Stsrisng.A01Y18
Match 2: The.Regex.F05P78
Match 3: Word.C05F78