(PHP 4, PHP 5, PHP 7, PHP
preg_match — Perform a regular expression match
Description
preg_match(
string $pattern
,
string $subject
,
array &$matches
= null
,
int $flags
= 0,
int $offset
= 0
): int|false
Parameters
-
pattern
-
The pattern to search for, as a string.
-
subject
-
The input string.
-
matches
-
If
matches
is provided, then it is filled with
the results of search. $matches[0] will contain the
text that matched the full pattern, $matches[1]
will have the text that matched the first captured parenthesized
subpattern, and so on. -
flags
-
flags
can be a combination of the following flags:-
PREG_OFFSET_CAPTURE
-
If this flag is passed, for every occurring match the appendant string
offset (in bytes) will also be returned. Note that this changes the value of
matches
into an array where every element is an
array consisting of the matched string at offset0
and its string offset intosubject
at offset
1
.
<?php
preg_match('/(foo)(bar)(baz)/', 'foobarbaz', $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
?>
The above example will output:
Array ( [0] => Array ( [0] => foobarbaz [1] => 0 ) [1] => Array ( [0] => foo [1] => 0 ) [2] => Array ( [0] => bar [1] => 3 ) [3] => Array ( [0] => baz [1] => 6 ) )
-
PREG_UNMATCHED_AS_NULL
-
If this flag is passed, unmatched subpatterns are reported as
null
;
otherwise they are reported as an empty string.
<?php
preg_match('/(a)(b)*(c)/', 'ac', $matches);
var_dump($matches);
preg_match('/(a)(b)*(c)/', 'ac', $matches, PREG_UNMATCHED_AS_NULL);
var_dump($matches);
?>
The above example will output:
array(4) { [0]=> string(2) "ac" [1]=> string(1) "a" [2]=> string(0) "" [3]=> string(1) "c" } array(4) { [0]=> string(2) "ac" [1]=> string(1) "a" [2]=> NULL [3]=> string(1) "c" }
-
-
offset
-
Normally, the search starts from the beginning of the subject string.
The optional parameteroffset
can be used to
specify the alternate place from which to start the search (in bytes).Note:
Using
offset
is not equivalent to passing
substr($subject, $offset)
to
preg_match() in place of the subject string,
becausepattern
can contain assertions such as
^, $ or
(?<=x). Compare:
<?php
$subject = "abcdef";
$pattern = '/^def/';
preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE, 3);
print_r($matches);
?>
The above example will output:
while this example
<?php
$subject = "abcdef";
$pattern = '/^def/';
preg_match($pattern, substr($subject,3), $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
?>
will produce
Array ( [0] => Array ( [0] => def [1] => 0 ) )
Alternatively, to avoid using substr(), use the
G
assertion rather than the^
anchor, or
theA
modifier instead, both of which work with
theoffset
parameter.
Return Values
preg_match() returns 1 if the pattern
matches given subject
, 0 if it does not, or false
on failure.
Warning
This function may
return Boolean false
, but may also return a non-Boolean value which
evaluates to false
. Please read the section on Booleans for more
information. Use the ===
operator for testing the return value of this
function.
Errors/Exceptions
If the regex pattern passed does not compile to a valid regex, an E_WARNING
is emitted.
Changelog
Version | Description |
---|---|
7.2.0 |
The PREG_UNMATCHED_AS_NULL is now supported for the$flags parameter.
|
Examples
Example #1 Find the string of text «php»
<?php
// The "i" after the pattern delimiter indicates a case-insensitive search
if (preg_match("/php/i", "PHP is the web scripting language of choice.")) {
echo "A match was found.";
} else {
echo "A match was not found.";
}
?>
Example #2 Find the word «web»
<?php
/* The b in the pattern indicates a word boundary, so only the distinct
* word "web" is matched, and not a word partial like "webbing" or "cobweb" */
if (preg_match("/bwebb/i", "PHP is the web scripting language of choice.")) {
echo "A match was found.";
} else {
echo "A match was not found.";
}
if (
preg_match("/bwebb/i", "PHP is the website scripting language of choice.")) {
echo "A match was found.";
} else {
echo "A match was not found.";
}
?>
Example #3 Getting the domain name out of a URL
<?php
// get host name from URL
preg_match('@^(?:http://)?([^/]+)@i',
"http://www.php.net/index.html", $matches);
$host = $matches[1];// get last two segments of host name
preg_match('/[^.]+.[^.]+$/', $host, $matches);
echo "domain name is: {$matches[0]}n";
?>
The above example will output:
Example #4 Using named subpattern
<?php
$str
= 'foobar: 2008';preg_match('/(?P<name>w+): (?P<digit>d+)/', $str, $matches);/* Alternative */
// preg_match('/(?<name>w+): (?<digit>d+)/', $str, $matches);print_r($matches);?>
The above example will output:
Array ( [0] => foobar: 2008 [name] => foobar [1] => foobar [digit] => 2008 [2] => 2008 )
Notes
Tip
Do not use preg_match() if you only want to check if
one string is contained in another string. Use
strpos() instead as it will be faster.
See Also
- PCRE Patterns
- preg_quote() — Quote regular expression characters
- preg_match_all() — Perform a global regular expression match
- preg_replace() — Perform a regular expression search and replace
- preg_split() — Split string by a regular expression
- preg_last_error() — Returns the error code of the last PCRE regex execution
- preg_last_error_msg() — Returns the error message of the last PCRE regex execution
force at md-t dot org ¶
11 years ago
Simple regex
Regex quick reference
[abc] A single character: a, b or c
[^abc] Any single character but a, b, or c
[a-z] Any single character in the range a-z
[a-zA-Z] Any single character in the range a-z or A-Z
^ Start of line
$ End of line
A Start of string
z End of string
. Any single character
s Any whitespace character
S Any non-whitespace character
d Any digit
D Any non-digit
w Any word character (letter, number, underscore)
W Any non-word character
b Any word boundary character
(...) Capture everything enclosed
(a|b) a or b
a? Zero or one of a
a* Zero or more of a
a+ One or more of a
a{3} Exactly 3 of a
a{3,} 3 or more of a
a{3,6} Between 3 and 6 of a
options: i case insensitive m make dot match newlines x ignore whitespace in regex o perform #{...} substitutions only once
MrBull ¶
12 years ago
Sometimes its useful to negate a string. The first method which comes to mind to do this is: [^(string)] but this of course won't work. There is a solution, but it is not very well known. This is the simple piece of code on how a negation of a string is done:
(?:(?!string).)
?: makes a subpattern (see http://www.php.net/manual/en/regexp.reference.subpatterns.php) and ?! is a negative look ahead. You put the negative look ahead in front of the dot because you want the regex engine to first check if there is an occurrence of the string you are negating. Only if it is not there, you want to match an arbitrary character.
Hope this helps some ppl.
yofilter-php at yahoo dot co dot uk ¶
10 years ago
There does not seem to be any mention of the PHP version of switches that can be used with regular expressions.
preg_match_all('/regular expr/sim',$text).
The s i m being the location for and available switches (I know about)
The i is to ignore letter cases (this is commonly known - I think)
The s tells the code NOT TO stop searching when it encounters n (line break) - this is important with multi-line entries for example text from an editor that needs search.
The m tells the code it is a multi-line entry, but importantly allows the use of ^ and $ to work when showing start and end.
I am hoping this will save someone from the 4 hours of torture that I endured, trying to workout this issue.
ruakuu at NOSPAM dot com ¶
13 years ago
Was working on a site that needed japanese and alphabetic letters and needed to
validate input using preg_match, I tried using p{script} but didn't work:
<?php
$pattern ='/^([-a-zA-Z0-9_p{Katakana}p{Hiragana}p{Han}]*)$/u'; // Didn't work
?>
So I tried with ranges and it worked:
<?php
$pattern ='/^[-a-zA-Z0-9_x{30A0}-x{30FF}'
.'x{3040}-x{309F}x{4E00}-x{9FBF}s]*$/u';
$match_string = '印刷最安 ニキビ跡除去 ゲームボーイ';
if (
preg_match($pattern, $match_string)) {
echo "Found - pattern $pattern";
} else {
echo "Not found - pattern $pattern";
}
?>
U+4E00–U+9FBF Kanji
U+3040–U+309F Hiragana
U+30A0–U+30FF Katakana
Hope its useful, it took me several hours to figure it out.
cebelab at gmail dot com ¶
13 years ago
I noticed that in order to deal with UTF-8 texts, without having to recompile php with the PCRE UTF-8 flag enabled, you can just add the following sequence at the start of your pattern: (*UTF8)
for instance : '#(*UTF8)[[:alnum:]]#' will return TRUE for 'é' where '#[[:alnum:]]#' will return FALSE
found this very very useful tip after hours of research over the web directly in pcre website right here : http://www.pcre.org/pcre.txt
there are many further informations about UTF-8 support in the lib
hop that will help!
--
cedric
arash dot hemmat at gmail dot com ¶
12 years ago
For those who search for a unicode regular expression example using preg_match here it is:
Check for Persian digits
preg_match( "/[^x{06F0}-x{06F9}x]+/u" , '۱۲۳۴۵۶۷۸۹۰' );
workhorse at op dot pl ¶
11 years ago
Preg_match returns empty result trying to validate $subject with carriege returns (/n/r).
To solve it one need to use /s modifier in $pattern string.
<?php
$pattern='/.*/s';
$valid=preg_match($pattern, $subject, $match);
?>
mohammad40g at gmail dot com ¶
11 years ago
This sample is for checking persian character:
<?php
preg_match("/[x{0600}-x{06FF}x]{1,32}/u", 'محمد');
?>
daevid at daevid dot com ¶
14 years ago
I just learned about named groups from a Python friend today and was curious if PHP supported them, guess what -- it does!!!
http://www.regular-expressions.info/named.html
<?php
preg_match("/(?P<foo>abc)(.*)(?P<bar>xyz)/",
'abcdefghijklmnopqrstuvwxyz',
$matches);
print_r($matches);
?>
will produce:
Array
(
[0] => abcdefghijklmnopqrstuvwxyz
[foo] => abc
[1] => abc
[2] => defghijklmnopqrstuvw
[bar] => xyz
[3] => xyz
)
Note that you actually get the named group as well as the numerical key
value too, so if you do use them, and you're counting array elements, be
aware that your array might be bigger than you initially expect it to be.
jonathan dot lydall at gmail dot removethispart dot com ¶
14 years ago
Because making a truly correct email validation function is harder than one may think, consider using this one which comes with PHP through the filter_var function (http://www.php.net/manual/en/function.filter-var.php):
<?php
$email = "someone@domain .local";
if(!
filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "E-mail is not valid";
} else {
echo "E-mail is valid";
}
?>
andre at koethur dot de ¶
9 years ago
Be aware of bug https://bugs.php.net/bug.php?id=50887 when using sub patterns: Un-matched optional sub patterns at the end won't show up in $matches.
Here is a workaround: Assign a name to all subpatterns you are interested in, and merge $match afterwards with an constant array containing some reasonable default values:
<?php
if (preg_match('/^(?P<lang>[^;*][^;]*){1}(?:;q=(?P<qval>[0-9.]+))?$/u', 'de', $match))
{
$match = array_merge(array('lang' => '', 'qval' => ''), $match);
print_r($match);
}
?>
This outputs:
Array
(
[lang] => de
[qval] =>
[0] => de
[1] => de
)
Instead of:
Array
(
[0] => de
[lang] => de
[1] => de
)
Kae Cyphet ¶
13 years ago
for those coming over from ereg, preg_match can be quite intimidating. to get started here is a migration tip.
<?php
if(ereg('[^0-9A-Za-z]',$test_string)) // will be true if characters arnt 0-9, A-Z or a-z.
if(preg_match('/[^0-9A-Za-z]/',$test_string)) // this is the preg_match version. the /'s are now required.
?>
solixmexico at outlook dot com ¶
6 years ago
To validate directorys on Windows i used this:
if( preg_match("#^([a-z]{1}:{1})?[\/]?([-w]+[\/]?)*$#i",$_GET['path'],$matches) !== 1 ){
echo("Invalid value");
}else{
echo("Valid value");
}
The parts are:
#^ and $i Make the string matches at all the pattern, from start to end for ensure a complete match.
([a-z]{1}:{1})? The string may starts with one letter and a colon, but only 1 character for eachone, this is for the drive letter (C:)
[\/]? The string may contain, but not require 1 slash or backslash after the drive letter, (/)
([-w]+[\/]?)* The string must have 1 or more of any character like hyphen, letter, number, underscore, and may contain a slash or back slash at the end, to have a directory like ("/" or "folderName" or "folderName/"), this may be repeated one or more times.
Anonymous ¶
10 years ago
Here is a function that decreases the numbers inside a string (useful to convert DOM object into simplexml object)
e.g.: decremente_chaine("somenode->anode[2]->achildnode[3]") will return "somenode->anode[1]->achildnode[2]"
the numbering of the nodes in simplexml starts from zero, but from 1 in DOM xpath objects
<?php
function decremente_chaine($chaine)
{
//récupérer toutes les occurrences de nombres et leurs indices
preg_match_all("/[0-9]+/",$chaine,$out,PREG_OFFSET_CAPTURE);
//parcourir les occurrences
for($i=0;$i<sizeof($out[0]);$i++)
{
$longueurnombre = strlen((string)$out[0][$i][0]);
$taillechaine = strlen($chaine);
// découper la chaine en 3 morceaux
$debut = substr($chaine,0,$out[0][$i][1]);
$milieu = ($out[0][$i][0])-1;
$fin = substr($chaine,$out[0][$i][1]+$longueurnombre,$taillechaine);
// si c'est 10,100,1000 etc. on décale tout de 1 car le résultat comporte un chiffre de moins
if(preg_match('#[1][0]+$#', $out[0][$i][0]))
{
for($j = $i+1;$j<sizeof($out[0]);$j++)
{
$out[0][$j][1] = $out[0][$j][1] -1;
}
}
$chaine = $debut.$milieu.$fin;
}
return $chaine;
}
?>
ulli dot luftpumpe at murkymind dot de ¶
11 years ago
Matching a backslash character can be confusing, because double escaping is needed in the pattern: first for PHP, second for the regex engine
<?php
//match newline control character:
preg_match('/n/','n'); //pattern matches and is stored as control character 0x0A in the pattern string
preg_match('/\n/','n'); //very same match, but is stored escaped as 0x5C,0x6E in the pattern string
//trying to match "'" (2 characters) in a text file, '\'' as PHP string:
$subject = file_get_contents('myfile.txt');
preg_match('/\'/',$subject); //DOESN'T MATCH!!! stored as 0x5C,0x27 (escaped apostrophe), this only matches apostrophe
preg_match('/\\'/',$subject); //matches, stored as 0x5C,0x5C,0x27 (escaped backslash and unescaped apostrophe)
preg_match('/\\\/',$subject); //also matches, stored as 0x5C,0x5C,0x5C,0x27 (escaped backslash and escaped apostrophe)
//matching "n" (2 characters):
preg_match('/\\n/','\n');
preg_match('/\n/','\n'); //same match - 3 backslashes are interpreted as 2 in PHP, if the following character is not escapeable
?>
cmallabon at homesfactory dot com ¶
11 years ago
Just an interesting note. Was just updating code to replace ereg() with strpos() and preg_match and the thought occured that preg_match() could be optimized to quit early when only searching if a string begins with something, for example
<?php
if(preg_match("/^http/", $url))
{
//do something
}
?>
vs
<?php
if(strpos($url, "http") === 0)
{
//do something
}
?>
As I guessed, strpos() is always faster (about 2x) for short strings like a URL but for very long strings of several paragraphs (e.g. a block of XML) when the string doesn't start with the needle preg_match as twice as fast as strpos() as it doesn't scan the entire string.
So, if you are searching long strings and expect it to normally be true (e.g. validating XML), strpos() is a much faster BUT if you expect if to often fail, preg_match is the better choice.
asdfasdasad34535 at iflow dot at ¶
9 years ago
Attention! PREG_OFFSET_CAPTURE not UTF-8 aware when using u modifier
and it's not a but, it's a feature:
https://bugs.php.net/bug.php?id=37391
Possible workaround: Use mb_strpos to get the correct offset, instead of the flag.
UTF-8 support would be nice.
sainnr at gmail dot com ¶
12 years ago
This sample regexp may be useful if you are working with DB field types.
(?P<type>w+)($|((?P<length>(d+|(.*)))))
For example, if you are have a such type as "varchar(255)" or "text", the next fragment
<?php
$type = 'varchar(255)'; // type of field
preg_match('/(?P<type>w+)($|((?P<length>(d+|(.*)))))/', $type, $field);
print_r($field);
?>
will output something like this:
Array ( [0] => varchar(255) [type] => varchar [1] => varchar [2] => (255) [length] => 255 [3] => 255 [4] => 255 )
ian_channing at hotmail dot com ¶
12 years ago
When trying to check a file path that could be windows or unix it took me quite a few tries to get the escape characters right.
The Unix directory separator must be escaped once and the windows directory separator must be escaped twice.
This will match path/to/file and pathtofile.exe
preg_match('/^[a-z0-9_./\]*$/i', $file_string);
splattermania at freenet dot de ¶
13 years ago
As I wasted lots of time finding a REAL regex for URLs and resulted in building it on my own, I now have found one, that seems to work for all kinds of urls:
<?php
$regex = "((https?|ftp)://)?"; // SCHEME
$regex .= "([a-z0-9+!*(),;?&=$_.-]+(:[a-z0-9+!*(),;?&=$_.-]+)?@)?"; // User and Pass
$regex .= "([a-z0-9-.]*).([a-z]{2,3})"; // Host or IP
$regex .= "(:[0-9]{2,5})?"; // Port
$regex .= "(/([a-z0-9+$_-].?)+)*/?"; // Path
$regex .= "(?[a-z+&$_.-][a-z0-9;:@&%=+/$_.-]*)?"; // GET Query
$regex .= "(#[a-z_.-][a-z0-9+$_.-]*)?"; // Anchor
?>
Then, the correct way to check against the regex ist as follows:
<?php
if(preg_match("/^$regex$/", $url))
{
return true;
}
?>
corey [works at] effim [delete] .com ¶
13 years ago
I see a lot of people trying to put together phone regex's and struggling (hey, no worries...they're complicated). Here's one that we use that's pretty nifty. It's not perfect, but it should work for most non-idealists.
*** Note: Only matches U.S. phone numbers. ***
<?php
// all on one line...
$regex = '/^(?:1(?:[. -])?)?(?:((?=d{3})))?([2-9]d{2})(?:(?<=(d{3})))? ?(?:(?<=d{3})[.-])?([2-9]d{2})[. -]?(d{4})(?: (?i:ext).? ?(d{1,5}))?$/';
// or broken up
$regex = '/^(?:1(?:[. -])?)?(?:((?=d{3})))?([2-9]d{2})'
.'(?:(?<=(d{3})))? ?(?:(?<=d{3})[.-])?([2-9]d{2})'
.'[. -]?(d{4})(?: (?i:ext).? ?(d{1,5}))?$/';
?>
If you're wondering why all the non-capturing subpatterns (which look like this "(?:", it's so that we can do this:
<?php
$formatted
= preg_replace($regex, '($1) $2-$3 ext. $4', $phoneNumber);
// or, provided you use the $matches argument in preg_match
$formatted = "($matches[1]) $matches[2]-$matches[3]";
if ($matches[4]) $formatted .= " $matches[4]";
?>
*** Results: ***
520-555-5542 :: MATCH
520.555.5542 :: MATCH
5205555542 :: MATCH
520 555 5542 :: MATCH
520) 555-5542 :: FAIL
(520 555-5542 :: FAIL
(520)555-5542 :: MATCH
(520) 555-5542 :: MATCH
(520) 555 5542 :: MATCH
520-555.5542 :: MATCH
520 555-0555 :: MATCH
(520)5555542 :: MATCH
520.555-4523 :: MATCH
19991114444 :: FAIL
19995554444 :: MATCH
514 555 1231 :: MATCH
1 555 555 5555 :: MATCH
1.555.555.5555 :: MATCH
1-555-555-5555 :: MATCH
520-555-5542 ext.123 :: MATCH
520.555.5542 EXT 123 :: MATCH
5205555542 Ext. 7712 :: MATCH
520 555 5542 ext 5 :: MATCH
520) 555-5542 :: FAIL
(520 555-5542 :: FAIL
(520)555-5542 ext .4 :: FAIL
(512) 555-1234 ext. 123 :: MATCH
1(555)555-5555 :: MATCH
danielrydell at gmail dot com ¶
5 years ago
When trying to match accented characters, such as those found in Spanish, there seems to be a different internal interpretation when using character classes. So the best way is to add the u option (for unicode) after the delimiters.
<?php//echoes 1 (adding u would not alter the result)
echo preg_match('/^áéíóúñ$/', 'áéíóúñ');//echoes 0 (unless with [ó]+ or [ó]* or adding u)
echo preg_match('/^áéí[ó]úñ$/', 'áéíóúñ');//so to match 'espana' or 'españa', add u or this won't match
//echoes 1
echo preg_match('/^espa[nñ]a$/u', 'españa');?>
akniep at rayo dot info ¶
14 years ago
Bugs of preg_match (PHP-version 5.2.5)
In most cases, the following example will show one of two PHP-bugs discovered with preg_match depending on your PHP-version and configuration.
<?php
$text
= "test=";
// creates a rather long text
for ($i = 0; $i++ < 100000;)
$text .= "%AB";// a typical URL_query validity-checker (the pattern's function does not matter for this example)
$pattern = '/^(?:[;/?:@&=+$,]|(?:[^W_]|[-_.!~*()[] ])|(?:%[da-fA-F]{2}))*$/';var_dump( preg_match( $pattern, $text ) );?>
Possible bug (1):
=============
On one of our Linux-Servers the above example crashes PHP-execution with a C(?) Segmentation Fault(!). This seems to be a known bug (see http://bugs.php.net/bug.php?id=40909), but I don't know if it has been fixed, yet.
If you are looking for a work-around, the following code-snippet is what I found helpful. It wraps the possibly crashing preg_match call by decreasing the PCRE recursion limit in order to result in a Reg-Exp error instead of a PHP-crash.
<?php
[...]// decrease the PCRE recursion limit for the (possibly dangerous) preg_match call
$former_recursion_limit = ini_set( "pcre.recursion_limit", 10000 );// the wrapped preg_match call
$result = preg_match( $pattern, $text );// reset the PCRE recursion limit to its original value
ini_set( "pcre.recursion_limit", $former_recursion_limit );// if the reg-exp fails due to the decreased recursion limit we may not make any statement, but PHP-execution continues
if ( PREG_RECURSION_LIMIT_ERROR === preg_last_error() )
{
// react on the failed regular expression here
$result = [...];// do logging or email-sending here
[...]
} //if?>
Possible bug (2):
=============
On one of our Windows-Servers the above example does not crash PHP, but (directly) hits the recursion-limit. Here, the problem is that preg_match does not return boolean(false) as expected by the description / manual of above.
In short, preg_match seems to return an int(0) instead of the expected boolean(false) if the regular expression could not be executed due to the PCRE recursion-limit. So, if preg_match results in int(0) you seem to have to check preg_last_error() if maybe an error occurred.
geompse at gmail dot com ¶
5 years ago
The function will return false and raise a warning if the input $subject is too long :
[PhpWarning] preg_match(): Subject is too long
I believe the limit is 1 or 2 GB because I was using a 2.2GB string.
While a parameter might exist to alter this limit, in my case it was possible and wiser to use <500MB strings instead.
aer0s ¶
11 years ago
Simple function to return a sub-string following the preg convention. Kind of expensive, and some might say lazy but it has saved me time.
# preg_substr($pattern,$subject,[$offset]) function
# @author aer0s
# return a specific sub-string in a string using
# a regular expression
# @param $pattern regular expression pattern to match
# @param $subject string to search
# @param [$offset] zero based match occurrence to return
#
# [$offset] is 0 by default which returns the first occurrence,
# if [$offset] is -1 it will return the last occurrence
function preg_substr($pattern,$subject,$offset=0){
preg_match_all($pattern,$subject,$matches,PREG_PATTERN_ORDER);
return $offset==-1?array_pop($matches[0]):$matches[0][$offset];
}
example:
$pattern = "/model(s|-)[a-z0-9]/i";
$subject = "Is there something wrong with model 654, Model 732, and model 43xl or is Model aj45B the preferred choice?";
echo preg_substr($pattern,$subject);
echo preg_substr($pattern,$subject,1);
echo preg_substr($pattern,$subject,-1);
Returns something like:
model 654
Model 732
Model aj45B
Nimja ¶
11 years ago
When using a 'bad words reject string' filter, preg_match is MUCH faster than strpos / stripos. Because in the other cases, you would need to do a foreach for each word. With efficient programming, the foreach is ONLY faster when the first word in the ban-list is found.
(for 12 words, 100,000 iterations, no word found)
stripos - Taken 1.4876 seconds.
strpos - Taken 1.4207 seconds.
preg_match - Taken 0.189 seconds.
Interesting fact:
With long words ('averylongwordtospitepreg'), the difference is only much less. Only about a 2/3rd of the time instead of 1/6th
<?php
$words
= array('word1', 'word2', 'word3', 'word4', 'word5', 'word6', 'word7', 'word8', 'word9', 'word10', 'word11', 'word12' );
$teststring = 'ThIs Is A tEsTsTrInG fOr TeStInG.';
$count = 100000;
$find = 0;$start = microtime(TRUE);
for ($i = 0; $i < $count; $i++) {
foreach ($words as $word) {
if (stripos($teststring, $word) !== FALSE) {
$find++;
break;
}
}
}
echo 'stripos - Taken ' . round(microtime(TRUE) - $start, 4) . ' seconds.' . PHP_EOL;$start = microtime(TRUE);
for ($i = 0; $i < $count; $i++) {
foreach ($words as $word) {
if (strpos($teststring, $word) !== FALSE) {
$find++;
break;
}
}
}
echo 'strpos - Taken ' . round(microtime(TRUE) - $start, 4) . ' seconds.' . PHP_EOL;$start = microtime(TRUE);
$pattern = '/';
$div = '';
foreach ($words as $word) {
$pattern .= $div . preg_quote($word);
$div = '|';
}
$pattern .= '/i';
//Pattern could easily be done somewhere else if words are static.
for ($i = 0; $i < $count; $i++) {
if (preg_match($pattern, $teststring)) {
$find++;
}
}
$end = microtime(TRUE);
echo 'preg_match - Taken ' . round($end - $start, 4) . ' seconds.' . PHP_EOL;
?>
SoN9ne at gmail dot com ¶
12 years ago
I have been working on a email system that will automatically generate a text email from a given HTML email by using strip_tags().
The only issue I ran into, for my needs, were that the anchors would not keep their links.
I search for a little while and could not find anything to strip the links from the tags so I generated my own little snippet.
I am posting it here in hopes that others may find it useful and for later reference.
A note to keep in mind:
I was primarily concerned with valid HTML so if attributes do no use ' or " to contain the values then this will need to be tweaked.
If you can edit this to work better, please let me know.
<?php
/**
* Replaces anchor tags with text
* - Will search string and replace all anchor tags with text (case insensitive)
*
* How it works:
* - Searches string for an anchor tag, checks to make sure it matches the criteria
* Anchor search criteria:
* - 1 - <a (must have the start of the anchor tag )
* - 2 - Can have any number of spaces or other attributes before and after the href attribute
* - 3 - Must close the anchor tag
*
* - Once the check has passed it will then replace the anchor tag with the string replacement
* - The string replacement can be customized
*
* Know issue:
* - This will not work for anchors that do not use a ' or " to contain the attributes.
* (i.e.- <a href=http: //php.net>PHP.net</a> will not be replaced)
*/
function replaceAnchorsWithText($data) {
/**
* Had to modify $regex so it could post to the site... so I broke it into 6 parts.
*/
$regex = '/(<as*'; // Start of anchor tag
$regex .= '(.*?)s*'; // Any attributes or spaces that may or may not exist
$regex .= 'href=['"]+?s*(?P<link>S+)s*['"]+?'; // Grab the link
$regex .= 's*(.*?)s*>s*'; // Any attributes or spaces that may or may not exist before closing tag
$regex .= '(?P<name>S+)'; // Grab the name
$regex .= 's*</a>)/i'; // Any number of spaces between the closing anchor tag (case insensitive)if (is_array($data)) {
// This is what will replace the link (modify to you liking)
$data = "{$data['name']}({$data['link']})";
}
return preg_replace_callback($regex, 'replaceAnchorsWithText', $data);
}$input = 'Test 1: <a href="http: //php.net1">PHP.NET1</a>.<br />';
$input .= 'Test 2: <A name="test" HREF='HTTP: //PHP.NET2' target="_blank">PHP.NET2</A>.<BR />';
$input .= 'Test 3: <a hRef=http: //php.net3>php.net3</a><br />';
$input .= 'This last line had nothing to do with any of this';
echo
replaceAnchorsWithText($input).'<hr/>';
?>
Will output:
Test 1: PHP.NET1(http: //php.net1).
Test 2: PHP.NET2(HTTP: //PHP.NET2).
Test 3: php.net3 (is still an anchor)
This last line had nothing to do with any of this
Posting to this site is painful...
Had to break up the regex and had to break the test links since it was being flagged as spam...
Yousef Ismaeil Cliprz ¶
10 years ago
Some times a Hacker use a php file or shell as a image to hack your website. so if you try to use move_uploaded_file() function as in example to allow for users to upload files, you must check if this file contains a bad codes or not so we use this function. preg match
in this function we use
unlink() - http://php.net/unlink
after you upload file check a file with below function.
<?php/**
* A simple function to check file from bad codes.
*
* @param (string) $file - file path.
* @author Yousef Ismaeil - Cliprz[at]gmail[dot]com.
*/
function is_clean_file ($file)
{
if (file_exists($file))
{
$contents = file_get_contents($file);
}
else
{
exit($file." Not exists.");
}
if (
preg_match('/(base64_|eval|system|shell_|exec|php_)/i',$contents))
{
return true;
}
else if (preg_match("#&#x([0-9a-f]+);#i", $contents))
{
return true;
}
elseif (preg_match('#&#([0-9]+);#i', $contents))
{
return true;
}
elseif (preg_match("#([a-z]*)=([`'"]*)script:#iU", $contents))
{
return true;
}
elseif (preg_match("#([a-z]*)=([`'"]*)javascript:#iU", $contents))
{
return true;
}
elseif (preg_match("#([a-z]*)=(['"]*)vbscript:#iU", $contents))
{
return true;
}
elseif (preg_match("#(<[^>]+)style=([`'"]*).*expression([^>]*>#iU", $contents))
{
return true;
}
elseif (preg_match("#(<[^>]+)style=([`'"]*).*behaviour([^>]*>#iU", $contents))
{
return true;
}
elseif (preg_match("#</*(applet|link|style|script|iframe|frame|frameset|html|body|title|div|p|form)[^>]*>#i", $contents))
{
return true;
}
else
{
return false;
}
}
?>
Use
<?php
// If image contains a bad codes
$image = "simpleimage.png";
if (
is_clean_file($image))
{
echo "Bad codes this is not image";
unlink($image);
}
else
{
echo "This is a real image.";
}
?>
Jonny 5 ¶
11 years ago
Workaround for getting the offset in UTF-8
(in some cases mb_strpos might be an option as well)
<?php
if(preg_match($pattern,$haystack,$out,PREG_OFFSET_CAPTURE)) {
$offset = strlen(utf8_decode(substr($haystack,0,$out[0][1])));
}
?>
ASchmidt at Anamera dot net ¶
3 years ago
After the breaking change in 7.4, be aware that count( $matches ) may be different, depending on PREG_UNMATCHED_AS_NULL flag.
With PREG_UNMATCHED_AS_NULL, count( $matches ) will always be the maximum number of subpatterns.
However, without PREG_UNMATCHED_AS_NULL the $matches array will omit any unmatched subpatterns at the tail end.
Result forthe first two out of three matches with PREG_OFFSET_CAPTURE flag only:
array (size=3)
0 =>
array (size=2)
0 => string 'may/02' (length=6)
1 => int 0
1 =>
array (size=2)
0 => string 'may' (length=3)
1 => int 0
2 =>
array (size=2)
0 => string '02' (length=2)
1 => int 4
Result for two out of three matches with additional PREG_UNMATCHED_AS_NULL flags:
array (size=4)
0 =>
array (size=2)
0 => string 'may/02' (length=6)
1 => int 0
1 =>
array (size=2)
0 => string 'may' (length=3)
1 => int 0
2 =>
array (size=2)
0 => string '02' (length=2)
1 => int 4
3 =>
array (size=2)
0 => null
1 => int -1
ian_channing at hotmail dot com ¶
13 years ago
This is a function that uses regular expressions to match against the various VAT formats required across the EU.
<?php
/**
* @param integer $country Country name
* @param integer $vat_number VAT number to test e.g. GB123 4567 89
* @return integer -1 if country not included OR 1 if the VAT Num matches for the country OR 0 if no match
*/
function checkVatNumber( $country, $vat_number ) {
switch($country) {
case 'Austria':
$regex = '/^(AT){0,1}U[0-9]{8}$/i';
break;
case 'Belgium':
$regex = '/^(BE){0,1}[0]{0,1}[0-9]{9}$/i';
break;
case 'Bulgaria':
$regex = '/^(BG){0,1}[0-9]{9,10}$/i';
break;
case 'Cyprus':
$regex = '/^(CY){0,1}[0-9]{8}[A-Z]$/i';
break;
case 'Czech Republic':
$regex = '/^(CZ){0,1}[0-9]{8,10}$/i';
break;
case 'Denmark':
$regex = '/^(DK){0,1}([0-9]{2}[ ]{0,1}){3}[0-9]{2}$/i';
break;
case 'Estonia':
case 'Germany':
case 'Greece':
case 'Portugal':
$regex = '/^(EE|EL|DE|PT){0,1}[0-9]{9}$/i';
break;
case 'France':
$regex = '/^(FR){0,1}[0-9A-Z]{2}[ ]{0,1}[0-9]{9}$/i';
break;
case 'Finland':
case 'Hungary':
case 'Luxembourg':
case 'Malta':
case 'Slovenia':
$regex = '/^(FI|HU|LU|MT|SI){0,1}[0-9]{8}$/i';
break;
case 'Ireland':
$regex = '/^(IE){0,1}[0-9][0-9A-Z+*][0-9]{5}[A-Z]$/i';
break;
case 'Italy':
case 'Latvia':
$regex = '/^(IT|LV){0,1}[0-9]{11}$/i';
break;
case 'Lithuania':
$regex = '/^(LT){0,1}([0-9]{9}|[0-9]{12})$/i';
break;
case 'Netherlands':
$regex = '/^(NL){0,1}[0-9]{9}B[0-9]{2}$/i';
break;
case 'Poland':
case 'Slovakia':
$regex = '/^(PL|SK){0,1}[0-9]{10}$/i';
break;
case 'Romania':
$regex = '/^(RO){0,1}[0-9]{2,10}$/i';
break;
case 'Sweden':
$regex = '/^(SE){0,1}[0-9]{12}$/i';
break;
case 'Spain':
$regex = '/^(ES){0,1}([0-9A-Z][0-9]{7}[A-Z])|([A-Z][0-9]{7}[0-9A-Z])$/i';
break;
case 'United Kingdom':
$regex = '/^(GB){0,1}([1-9][0-9]{2}[ ]{0,1}[0-9]{4}[ ]{0,1}[0-9]{2})|([1-9][0-9]{2}[ ]{0,1}[0-9]{4}[ ]{0,1}[0-9]{2}[ ]{0,1}[0-9]{3})|((GD|HA)[0-9]{3})$/i';
break;
default:
return -1;
break;
}
return
preg_match($regex, $vat_number);
}
?>
Frank ¶
12 years ago
If someone is from a country that accepts decimal numbers in format 9.00 and 9,00 (point or comma), number validation would be like that:
<?php
$number_check = "9,99";
if (preg_match( '/^[-+]?[0-9]*.*,?[0-9]+$/', $number_check)) {
return TRUE;
}
?>
However, if the number will be written in the database, most probably this comma needs to be replaced with a dot.
This can be done with use of str_replace, i.e :
<?php
$number_database = str_replace("," , "." , $number_check);
?>
matt ¶
13 years ago
To support large Unicode ranges (ie: [x{E000}-x{FFFD}] or x{10FFFFF}) you must use the modifier '/u' at the end of your expression.
plasma ¶
13 years ago
To extract scheme, host, path, ect. simply use
<?php
$url
= 'http://name:pass@';
$url .= 'example.com:10000';
$url .= '/path/to/file.php?a=1&b=2#anchor';
$url_data = parse_url ( $url );
print_r ( $url_data );
?>
___
prints out something like:
Array
(
[scheme] => http
[host] => wild.subdomain.orgy.domain.co.uk
[port] => 10000
[user] => name
[pass] => pass
[path] => /path/to/file.php
[query] => a=1&b=2
[fragment] => anchor
)
In my tests parse_url is up to 15x faster than preg_match(_all)!
Stefan ¶
13 years ago
I spent a while replacing all my ereg() calls to preg_match(), since ereg() is now deprecated and will not be supported as of v 6.0.
Just a warning regarding the conversion, the two functions behave very similarly, but not exactly alike. Obviously, you will need to delimit your pattern with '/' or '|' characters.
The difference that stumped me was that preg_replace overwrites the $matches array regardless if a match was found. If no match was found, $matches is simply empty.
ereg(), however, would leave $matches alone if a match was not found. In my code, I had repeated calls to ereg, and was populating $matches with each match. I was only interested in the last match. However, with preg_match, if the very last call to the function did not result in a match, the $matches array would be overwritten with a blank value.
Here is an example code snippet to illustrate:
<?php
$test = array('yes','no','yes','no','yes','no');
foreach (
$test as $key=>$value) {
ereg("yes",$value,$matches1);
preg_match("|yes|",$value,$matches2);
}
print "ereg result: $matches1[0]<br>";
print "preg_match result: $matches2[0]<br>";
?>
The output is:
ereg result: yes
preg_match result:
($matches2[0] in this case is empty)
I believe the preg_match behavior is cleaner. I just thought I would report this to hopefully save others some time.
wjaspers4 [at] gmail [dot] com ¶
14 years ago
I recently encountered a problem trying to capture multiple instances of named subpatterns from filenames.
Therefore, I came up with this function.
The function allows you to pass through flags (in this version it applies to all expressions tested), and generates an array of search results.
Enjoy!
<?php/**
* Allows multiple expressions to be tested on one string.
* This will return a boolean, however you may want to alter this.
*
* @author William Jaspers, IV <wjaspers4@gmail.com>
* @created 2009-02-27 17:00:00 +6:00:00 GMT
* @access public
*
* @param array $patterns An array of expressions to be tested.
* @param String $subject The data to test.
* @param array $findings Optional argument to store our results.
* @param mixed $flags Pass-thru argument to allow normal flags to apply to all tested expressions.
* @param array $errors A storage bin for errors
*
* @returns bool Whether or not errors occurred.
*/
function preg_match_multiple(
array $patterns=array(),
$subject=null,
&$findings=array(),
$flags=false,
&$errors=array()
) {
foreach( $patterns as $name => $pattern )
{
if( 1 <= preg_match_all( $pattern, $subject, $found, $flags ) )
{
$findings[$name] = $found;
} else
{
if( PREG_NO_ERROR !== ( $code = preg_last_error() ))
{
$errors[$name] = $code;
} else $findings[$name] = array();
}
}
return (0===sizeof($errors));
}
?>
itworkarounds at gmail dot com ¶
11 years ago
You can use the following code to detect non-latin (Cyrilic, Arabic, Greek...) characters:
<?php
preg_match("/^[a-zA-Zp{Cyrillic}0-9s-]+$/u", "ABC abc 1234 АБВ абв");
?>
matt at proweb dot co dot uk ¶
2 years ago
pcre2-migration
Status: Implemented (in PHP 7.3)
SELinux will prevent PREG_* functions from working
Feb 8 12:40:51 servername setroubleshoot: SELinux is preventing httpd from using the execmem access on a process.
you need to add preg.jit=0 to php.ini or init_set('preg.jit', 0) if you can't do that
try the [PCRE] section so you can find it
skds1433 at hotmail dot com ¶
14 years ago
here is a small tool for someone learning to use regular expressions. it's very basic, and allows you to try different patterns and combinations. I made it to help me, because I like to try different things, to get a good understanding of how things work.
<?php
$search = isset($_POST['search'])?$_POST['search']:"//";
$match = isset($_POST['match'])?$_POST['match']:"<>";
echo
'<form method="post">';
echo 's: <input style="width:400px;" name="search" type="text" value="'.$search.'" /><br />';
echo 'm:<input style="width:400px;" name="match" type="text" value="'.$match.'" /><input type="submit" value="go" /></form><br />';
if (preg_match($search, $match)){echo "matches";}else{echo "no match";}
?>
marcosc at tekar dot net ¶
13 years ago
When using accented characters and "ñ" (áéíóúñ), preg_match does not work. It is a charset problem, use utf8_decode/decode to fix.
chris at ocproducts dot com ¶
2 years ago
If PREG_OFFSET_CAPTURE is set then unmatched captures (i.e. ones with '?') will not be present in the result array. This is presumably because there is no offset, and thus the original PHP dev decided best to just leave it out.
ayman2243 at gmail dot com ¶
11 years ago
highlight Search Words
<?php
function highlight($word, $subject) {
$split_subject = explode(" ", $subject);
$split_word = explode(" ", $word);
foreach (
$split_subject as $k => $v){
foreach ($split_word as $k2 => $v2){
if($v2 == $v){
$split_subject[$k] = "<span class='highlight'>".$v."</span>";
}
}
}
return
implode(' ', $split_subject);
}
?>
teracci2002 ¶
13 years ago
ASchmidt at Anamera dot net ¶
3 years ago
Combining flags
PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL
will NOT result in a value of NULL for any submatched subpattern.
Instead still results in an array for each unmatched subpattern, in turn containing:
array (size=2)
0 => null
1 => int -1
Consequently your code needs to expect NULL as the string value:
$matches[ {subpattern+1} ][0] === null
and/or a negative string offset:
$matches[ {subpattern+1} ][1] < 0
to detect any unmatched subpattern!
chat dot noir at arcor dot de ¶
5 years ago
Note that if a parenthesed group is not matched, its key may or may not be present in $matches. For instance,
<?php preg_match('/(foo)?(bar)?(baz)?/', 'bar', $matches);
print_r($matches); // outputs
// Array
// (
// [0] => bar
// [1] =>
// [2] => bar
// )
?>
Note that there is no element with key '3' in $matches, but an element with key '1' (the empty string). This inconsistent behavior also applies to named groups.
jphansen at uga dot edu ¶
10 years ago
Here's a regex to validate against the schema for common MySQL
identifiers:
<?php
$string = "$table_name";
if (preg_match("/[^\d\sa-zA-Z$_]/", $string))
echo "Failed validation";
?>
Anonymous ¶
13 years ago
The regular expression for breaking-down a URI reference into its components:
^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(?([^#]*))?(#(.*))?
12 3 4 5 6 7 8 9
Source: ietf.org/rfc/rfc2396.txt
xcsv at gmx dot net ¶
2 years ago
As of PHP 7.2, you can use the following.
If you work with named subpatterns and dont want to bother with unnamed match result entries and unmatched subpatterns, just replace preg_match() with named_preg_match(). This filters all unwanted stuff out.
<?php
function named_preg_match(string $pattern , string $subject, array &$matches = null, int $flags = 0, int $offset = 0) {
$retval = preg_match($pattern, $subject, $localmatches, PREG_UNMATCHED_AS_NULL | $flags, $offset);
if ($retval) {
foreach ($localmatches as $key => $value) {
if (is_int($key)) $value = null;
if (is_null($value)) unset($localmatches[$key]);
}
$matches = $localmatches;
}
return $retval;
}
?>
Hope this will be useful.
phil dot taylor at gmail dot com ¶
14 years ago
If you need to check for .com.br and .com.au and .uk and all the other crazy domain endings i found the following expression works well if you want to validate an email address. Its quite generous in what it will allow
<?php
$email_address
= "phil.taylor@a_domain.tv";
if (
preg_match("/^[^@]*@[^@]*.[^@]*$/", $email_address)) {
return "E-mail address";
}
?>
sun at drupal dot org ¶
11 years ago
(PHP 4, PHP 5, PHP 7, PHP
preg_match_all — Perform a global regular expression match
Description
preg_match_all(
string $pattern
,
string $subject
,
array &$matches
= null
,
int $flags
= 0,
int $offset
= 0
): int|false
After the first match is found, the subsequent searches are continued
on from end of the last match.
Parameters
-
pattern
-
The pattern to search for, as a string.
-
subject
-
The input string.
-
matches
-
Array of all matches in multi-dimensional array ordered according to
flags
. -
flags
-
Can be a combination of the following flags (note that it doesn’t make
sense to usePREG_PATTERN_ORDER
together with
PREG_SET_ORDER
):-
PREG_PATTERN_ORDER
-
Orders results so that $matches[0] is an array of full
pattern matches, $matches[1] is an array of strings matched by
the first parenthesized subpattern, and so on.
<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
"<b>example: </b><div align=left>this is a test</div>",
$out, PREG_PATTERN_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "n";
echo $out[1][0] . ", " . $out[1][1] . "n";
?>
The above example will output:
<b>example: </b>, <div align=left>this is a test</div> example: , this is a test
So, $out[0] contains array of strings that matched full pattern,
and $out[1] contains array of strings enclosed by tags.If the pattern contains named subpatterns, $matches
additionally contains entries for keys with the subpattern name.If the pattern contains duplicate named subpatterns, only the rightmost
subpattern is stored in $matches[NAME].
<?php
preg_match_all(
'/(?J)(?<match>foo)|(?<match>bar)/',
'foo bar',
$matches,
PREG_PATTERN_ORDER
);
print_r($matches['match']);
?>
The above example will output:
Array ( [0] => [1] => bar )
-
PREG_SET_ORDER
-
Orders results so that $matches[0] is an array of first set
of matches, $matches[1] is an array of second set of matches,
and so on.
<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
"<b>example: </b><div align="left">this is a test</div>",
$out, PREG_SET_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "n";
echo $out[1][0] . ", " . $out[1][1] . "n";
?>
The above example will output:
<b>example: </b>, example: <div align="left">this is a test</div>, this is a test
-
PREG_OFFSET_CAPTURE
-
If this flag is passed, for every occurring match the appendant string
offset (in bytes) will also be returned. Note that this changes the value of
matches
into an array of arrays where every element is an
array consisting of the matched string at offset0
and its string offset intosubject
at offset
1
.
<?php
preg_match_all('/(foo)(bar)(baz)/', 'foobarbaz', $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
?>
The above example will output:
Array ( [0] => Array ( [0] => Array ( [0] => foobarbaz [1] => 0 ) ) [1] => Array ( [0] => Array ( [0] => foo [1] => 0 ) ) [2] => Array ( [0] => Array ( [0] => bar [1] => 3 ) ) [3] => Array ( [0] => Array ( [0] => baz [1] => 6 ) ) )
-
PREG_UNMATCHED_AS_NULL
-
If this flag is passed, unmatched subpatterns are reported as
null
;
otherwise they are reported as an empty string.
If no order flag is given,
PREG_PATTERN_ORDER
is
assumed. -
-
offset
-
Normally, the search starts from the beginning of the subject string.
The optional parameteroffset
can be used to
specify the alternate place from which to start the search (in bytes).Note:
Using
offset
is not equivalent to passing
substr($subject, $offset)
to
preg_match_all() in place of the subject string,
becausepattern
can contain assertions such as
^, $ or
(?<=x). See preg_match()
for examples.
Return Values
Returns the number of full pattern matches (which might be zero), or false
on failure.
Errors/Exceptions
If the regex pattern passed does not compile to a valid regex, an E_WARNING
is emitted.
Changelog
Version | Description |
---|---|
7.2.0 |
The PREG_UNMATCHED_AS_NULL is now supported for the$flags parameter.
|
Examples
Example #1 Getting all phone numbers out of some text.
<?php
preg_match_all("/(? (d{3})? )? (?(1) [-s] ) d{3}-d{4}/x",
"Call 555-1212 or 1-800-555-1212", $phones);
?>
Example #2 Find matching HTML tags (greedy)
<?php
// The \2 is an example of backreferencing. This tells pcre that
// it must match the second set of parentheses in the regular expression
// itself, which would be the ([w]+) in this case. The extra backslash is
// required because the string is in double quotes.
$html = "<b>bold text</b><a href=howdy.html>click me</a>";preg_match_all("/(<([w]+)[^>]*>)(.*?)(</\2>)/", $html, $matches, PREG_SET_ORDER);
foreach (
$matches as $val) {
echo "matched: " . $val[0] . "n";
echo "part 1: " . $val[1] . "n";
echo "part 2: " . $val[2] . "n";
echo "part 3: " . $val[3] . "n";
echo "part 4: " . $val[4] . "nn";
}
?>
The above example will output:
matched: <b>bold text</b> part 1: <b> part 2: b part 3: bold text part 4: </b> matched: <a href=howdy.html>click me</a> part 1: <a href=howdy.html> part 2: a part 3: click me part 4: </a>
Example #3 Using named subpattern
<?php
$str
= <<<FOO
a: 1
b: 2
c: 3
FOO;preg_match_all('/(?P<name>w+): (?P<digit>d+)/', $str, $matches);/* Alternative */
// preg_match_all('/(?<name>w+): (?<digit>d+)/', $str, $matches);print_r($matches);?>
The above example will output:
Array ( [0] => Array ( [0] => a: 1 [1] => b: 2 [2] => c: 3 ) [name] => Array ( [0] => a [1] => b [2] => c ) [1] => Array ( [0] => a [1] => b [2] => c ) [digit] => Array ( [0] => 1 [1] => 2 [2] => 3 ) [2] => Array ( [0] => 1 [1] => 2 [2] => 3 ) )
See Also
- PCRE Patterns
- preg_quote() — Quote regular expression characters
- preg_match() — Perform a regular expression match
- preg_replace() — Perform a regular expression search and replace
- preg_split() — Split string by a regular expression
- preg_last_error() — Returns the error code of the last PCRE regex execution
buuh ¶
12 years ago
if you want to extract all {token}s from a string:
<?php
$pattern = "/{[^}]*}/";
$subject = "{token1} foo {token2} bar";
preg_match_all($pattern, $subject, $matches);
print_r($matches);
?>
output:
Array
(
[0] => Array
(
[0] => {token1}
[1] => {token2}
)
)
Daniel Klein ¶
7 years ago
The code that john at mccarthy dot net posted is not necessary. If you want your results grouped by individual match simply use:
<?
preg_match_all($pattern, $string, $matches, PREG_SET_ORDER);
?>
E.g.
<?
preg_match_all('/([GH])([12])([!?])/', 'G1? H2!', $matches); // Default PREG_PATTERN_ORDER
// $matches = array(0 => array(0 => 'G1?', 1 => 'H2!'),
// 1 => array(0 => 'G', 1 => 'H'),
// 2 => array(0 => '1', 1 => '2'),
// 3 => array(0 => '?', 1 => '!'))
preg_match_all('/([GH])([12])([!?])/', 'G1? H2!', $matches, PREG_SET_ORDER);
// $matches = array(0 => array(0 => 'G1?', 1 => 'G', 2 => '1', 3 => '?'),
// 1 => array(0 => 'H2!', 1 => 'H', 2 => '2', 3 => '!'))
?>
mnc at u dot nu ¶
17 years ago
PREG_OFFSET_CAPTURE always seems to provide byte offsets, rather than character position offsets, even when you are using the unicode /u modifier.
stas kuryan aka stafox ¶
7 years ago
Here is a awesome online regex editor https://regex101.com/
which helps you test your regular expressions (prce, js, python) with real-time highlighting of regex match on data input.
phpnet at sinful-music dot com ¶
17 years ago
Here's some fleecy code to 1. validate RCF2822 conformity of address lists and 2. to extract the address specification (the part commonly known as 'email'). I wouldn't suggest using it for input form email checking, but it might be just what you want for other email applications. I know it can be optimized further, but that part I'll leave up to you nutcrackers. The total length of the resulting Regex is about 30000 bytes. That because it accepts comments. You can remove that by setting $cfws to $fws and it shrinks to about 6000 bytes. Conformity checking is absolutely and strictly referring to RFC2822. Have fun and email me if you have any enhancements!
<?php
function mime_extract_rfc2822_address($string)
{
//rfc2822 token setup
$crlf = "(?:rn)";
$wsp = "[t ]";
$text = "[\x01-\x09\x0B\x0C\x0E-\x7F]";
$quoted_pair = "(?:\\$text)";
$fws = "(?:(?:$wsp*$crlf)?$wsp+)";
$ctext = "[\x01-\x08\x0B\x0C\x0E-\x1F" .
"!-'*-[\]-\x7F]";
$comment = "(\((?:$fws?(?:$ctext|$quoted_pair|(?1)))*" .
"$fws?\))";
$cfws = "(?:(?:$fws?$comment)*(?:(?:$fws?$comment)|$fws))";
//$cfws = $fws; //an alternative to comments
$atext = "[!#-'*+\-\/0-9=?A-Z\^-~]";
$atom = "(?:$cfws?$atext+$cfws?)";
$dot_atom_text = "(?:$atext+(?:\.$atext+)*)";
$dot_atom = "(?:$cfws?$dot_atom_text$cfws?)";
$qtext = "[\x01-\x08\x0B\x0C\x0E-\x1F!#-[\]-\x7F]";
$qcontent = "(?:$qtext|$quoted_pair)";
$quoted_string = "(?:$cfws?"(?:$fws?$qcontent)*$fws?"$cfws?)";
$dtext = "[\x01-\x08\x0B\x0C\x0E-\x1F!-Z\^-\x7F]";
$dcontent = "(?:$dtext|$quoted_pair)";
$domain_literal = "(?:$cfws?\[(?:$fws?$dcontent)*$fws?]$cfws?)";
$domain = "(?:$dot_atom|$domain_literal)";
$local_part = "(?:$dot_atom|$quoted_string)";
$addr_spec = "($local_part@$domain)";
$display_name = "(?:(?:$atom|$quoted_string)+)";
$angle_addr = "(?:$cfws?<$addr_spec>$cfws?)";
$name_addr = "(?:$display_name?$angle_addr)";
$mailbox = "(?:$name_addr|$addr_spec)";
$mailbox_list = "(?:(?:(?:(?<=:)|,)$mailbox)+)";
$group = "(?:$display_name:(?:$mailbox_list|$cfws)?;$cfws?)";
$address = "(?:$mailbox|$group)";
$address_list = "(?:(?:^|,)$address)+";//output length of string (just so you see how f**king long it is)
echo(strlen($address_list) . " ");//apply expression
preg_match_all("/^$address_list$/", $string, $array, PREG_SET_ORDER);
return
$array;
};
?>
harrybarrow at mail dot ru ¶
1 year ago
preg_match_all() and other preg_*() functions doesn't work well with very long strings, at least longer that 1Mb.
In this case case function returns FALSE and $matchers value is unpredictable, may contain some values, may be empty.
In this case workaround is pre-split long string onto parts, for instance explode() long string by some criteria and then apply preg_match_all() on each part.
Typical scenario for this case is log analysis by regular expressions.
Tested on PHP 7.2.0
bruha ¶
15 years ago
To count str_length in UTF-8 string i use
$count = preg_match_all("/[[:print:]pL]/u", $str, $pockets);
where
[:print:] - printing characters, including space
pL - UTF-8 Letter
/u - UTF-8 string
other unicode character properties on http://www.pcre.org/pcre.txt
fab ¶
10 years ago
Here is a function that replaces all occurrences of a number in a string by the number--
<?php
function decremente_chaine($chaine)
{
//récupérer toutes les occurrences de nombres et leurs indices
preg_match_all("/[0-9]+/",$chaine,$out,PREG_OFFSET_CAPTURE);
//parcourir les occurrences
for($i=0;$i<sizeof($out[0]);$i++)
{
$longueurnombre = strlen((string)$out[0][$i][0]);
$taillechaine = strlen($chaine);
// découper la chaine en 3 morceaux
$debut = substr($chaine,0,$out[0][$i][1]);
$milieu = ($out[0][$i][0])-1;
$fin = substr($chaine,$out[0][$i][1]+$longueurnombre,$taillechaine);
// si c'est 10,100,1000 etc. on décale tout de 1 car le résultat comporte un chiffre de moins
if(preg_match('#[1][0]+$#', $out[0][$i][0]))
{
for($j = $i+1;$j<sizeof($out[0]);$j++)
{
$out[0][$j][1] = $out[0][$j][1] -1;
}
}
$chaine = $debut.$milieu.$fin;
}
return $chaine;
}
?>
chuckie ¶
16 years ago
This is a function to convert byte offsets into (UTF-8) character offsets (this is reagardless of whether you use /u modifier:
<?phpfunction mb_preg_match_all($ps_pattern, $ps_subject, &$pa_matches, $pn_flags = PREG_PATTERN_ORDER, $pn_offset = 0, $ps_encoding = NULL) {
// WARNING! - All this function does is to correct offsets, nothing else:
//
if (is_null($ps_encoding))
$ps_encoding = mb_internal_encoding();$pn_offset = strlen(mb_substr($ps_subject, 0, $pn_offset, $ps_encoding));
$ret = preg_match_all($ps_pattern, $ps_subject, $pa_matches, $pn_flags, $pn_offset);
if (
$ret && ($pn_flags & PREG_OFFSET_CAPTURE))
foreach($pa_matches as &$ha_match)
foreach($ha_match as &$ha_match)
$ha_match[1] = mb_strlen(substr($ps_subject, 0, $ha_match[1]), $ps_encoding);
//
// (code is independent of PREG_PATTER_ORDER / PREG_SET_ORDER)return $ret;
}?>
meaneye at mail dot com ¶
14 years ago
Recently I had to write search engine in hebrew and ran into huge amount of problems. My data was stored in MySQL table with utf8_bin encoding.
So, to be able to write hebrew in utf8 table you need to do
<?php
$prepared_text = addslashes(urf8_encode($text));
?>
But then I had to find if some word exists in stored text. This is the place I got stuck. Simple preg_match would not find text since hebrew doesnt work that easy. I've tried with /u and who kows what else.
Solution was somewhat logical and simple...
<?php
$db_text = bin2hex(stripslashes(utf8_decode($db_text)));
$word = bin2hex($word);$found = preg_match_all("/($word)+/i", $db_text, $matches);
?>
I've used preg_match_all since it returns number of occurences. So I could sort search results acording to that.
Hope someone finds this useful!
spambegone at cratemedia dot com ¶
14 years ago
I found simpleXML to be useful only in cases where the XML was extremely small, otherwise the server would run out of memory (I suspect there is a memory leak or something?). So while searching for alternative parsers, I decided to try a simpler approach. I don't know how this compares with cpu usage, but I know it works with large XML structures. This is more a manual method, but it works for me since I always know what structure of data I will be receiving.
Essentially I just preg_match() unique nodes to find the values I am looking for, or I preg_match_all to find multiple nodes. This puts the results in an array and I can then process this data as I please.
I was unhappy though, that preg_match_all() stores the data twice (requiring twice the memory), one array for all the full pattern matches, and one array for all the sub pattern matches. You could probably write your own function that overcame this. But for now this works for me, and I hope it saves someone else some time as well.
// SAMPLE XML
<RETS ReplyCode="0" ReplyText="Operation Successful">
<COUNT Records="14" />
<DELIMITER value="09" />
<COLUMNS>PropertyID</COLUMNS>
<DATA>521897</DATA>
<DATA>677208</DATA>
<DATA>686037</DATA>
</RETS>
<?PHP
// SAMPLE FUNCTION
function parse_xml($xml) {
// GET DELIMITER (single instance)
$match_res = preg_match('/<DELIMITER value ?= ?"(.*)" ?/>/', $xml, $matches);
if(!empty($matches[1])) {
$results["delimiter"] = chr($matches[1]);
} else {
// DEFAULT DELIMITER
$results["delimiter"] = "t";
}
unset($match_res, $matches);
// GET MULTIPLE DATA NODES (multiple instances)
$results["data_count"] = preg_match_all("/<DATA>(.*)</DATA>/", $xml, $matches);
// GET MATCHES OF SUB PATTERN, DISCARD THE REST
$results["data"]=$matches[1];
unset($match_res, $matches);
// UNSET XML TO SAVE MEMORY (should unset outside the function as well)
unset($xml);
// RETURN RESULTS ARRAY
return $results;
}
?>
qdinar at gmail dot com ¶
4 years ago
when regex is for longer and shorter version of a string,
only one of that long and short versions is catched.
when regex match occurs in one position of string,
only one match is saved in matches[0] for that position.
if ? is used, regex is greedy, and catches more long version,
if | is used, most first matching variant is catched:
<?php
preg_match_all('/ab|abc/','abc',$m);
var_dump($m);
preg_match_all('/abc?/','abc',$m);
var_dump($m);
?>
['ab', 'abc'] in $m[0] for both can be expected, but it is not so,
actually they output [['ab']] and [['abc']]:
array(1) {
[0]=>
array(1) {
[0]=>
string(2) "ab"
}
}
array(1) {
[0]=>
array(1) {
[0]=>
string(3) "abc"
}
}
no at bo dot dy ¶
12 years ago
For parsing queries with entities use:
<?php
preg_match_all("/(?:^|(?<=&(?![a-z]+;)))([^=]+)=(.*?)(?:$|&(?![a-z]+;))/i",
$s, $m, PREG_SET_ORDER );
?>
ad ¶
14 years ago
i have made up a simple function to extract a number from a string..
I am not sure how good it is, but it works.
It gets only the numbers 0-9, the "-", " ", "(", ")", "."
characters.. This is as far as I know the most widely used characters for a Phone number.
<?php
function clean_phone_number($phone) {
if (!empty($phone)) {
//var_dump($phone);
preg_match_all('/[0-9()+.- ]/s', $phone, $cleaned);
foreach($cleaned[0] as $k=>$v) {
$ready .= $v;
}
var_dump($ready);
die;
if (mb_strlen($cleaned) > 4 && mb_strlen($cleaned) <=25) {
return $cleaned;
}
else {
return false;
}
}
return false;
}
?>
john at mccarthy dot net ¶
12 years ago
I needed a function to rotate the results of a preg_match_all query, and made this. Not sure if it exists.
<?php
function turn_array($m)
{
for ($z = 0;$z < count($m);$z++)
{
for ($x = 0;$x < count($m[$z]);$x++)
{
$rt[$x][$z] = $m[$z][$x];
}
}
return
$rt;
}
?>
Example - Take results of some preg_match_all query:
Array
(
[0] => Array
(
[1] => Banff
[2] => Canmore
[3] => Invermere
)
[1] => Array
(
[1] => AB
[2] => AB
[3] => BC
)
[2] => Array
(
[1] => 51.1746254
[2] => 51.0938416
[3] => 50.5065193
)
[3] => Array
(
[1] => -115.5719757
[2] => -115.3517761
[3] => -116.0321884
)
[4] => Array
(
[1] => T1L 1B3
[2] => T1W 1N2
[3] => V0B 2G0
)
)
Rotate it 90 degrees to group results as records:
Array
(
[0] => Array
(
[1] => Banff
[2] => AB
[3] => 51.1746254
[4] => -115.5719757
[5] => T1L 1B3
)
[1] => Array
(
[1] => Canmore
[2] => AB
[3] => 51.0938416
[4] => -115.3517761
[5] => T1W 1N2
)
[2] => Array
(
[1] => Invermere
[2] => BC
[3] => 50.5065193
[4] => -116.0321884
[5] => V0B 2G0
)
)
sledge NOSPAM ¶
14 years ago
Perhaps you want to find the positions of all anchor tags. This will return a two dimensional array of which the starting and ending positions will be returned.
<?php
function getTagPositions($strBody)
{
define(DEBUG, false);
define(DEBUG_FILE_PREFIX, "/tmp/findlinks_");preg_match_all("/<[^>]+>(.*)</[^>]+>/U", $strBody, $strTag, PREG_PATTERN_ORDER);
$intOffset = 0;
$intIndex = 0;
$intTagPositions = array();
foreach(
$strTag[0] as $strFullTag) {
if(DEBUG == true) {
$fhDebug = fopen(DEBUG_FILE_PREFIX.time(), "a");
fwrite($fhDebug, $fulltag."n");
fwrite($fhDebug, "Starting position: ".strpos($strBody, $strFullTag, $intOffset)."n");
fwrite($fhDebug, "Ending position: ".(strpos($strBody, $strFullTag, $intOffset) + strlen($strFullTag))."n");
fwrite($fhDebug, "Length: ".strlen($strFullTag)."nn");
fclose($fhDebug);
}
$intTagPositions[$intIndex] = array('start' => (strpos($strBody, $strFullTag, $intOffset)), 'end' => (strpos($strBody, $strFullTag, $intOffset) + strlen($strFullTag)));
$intOffset += strlen($strFullTag);
$intIndex++;
}
return $intTagPositions;
}$strBody = 'I have lots of <a href="http://my.site.com">links</a> on this <a href="http://my.site.com">page</a> that I want to <a href="http://my.site.com">find</a> the positions.';$strBody = strip_tags(html_entity_decode($strBody), '<a>');
$intTagPositions = getTagPositions($strBody);
print_r($intTagPositions);/*****
Output:
Array (
[0] => Array (
[start] => 15
[end] => 53 )
[1] => Array (
[start] => 62
[end] => 99 )
[2] => Array (
[start] => 115
[end] => 152 )
)
*****/
?>
stamster at gmail dot com ¶
7 years ago
Be careful with this pattern match and large input buffer on preg_match_* functions.
<?php
$pattern = '/{(?:[^{}]|(?R))*}/';preg_match_all($pattern, $buffer, $matches);
?>
if $buffer is 80+ KB in size, you'll end up with segfault!
[89396.588854] php[4384]: segfault at 7ffd6e2bdeb0 ip 00007fa20c8d67ed sp 00007ffd6e2bde70 error 6 in libpcre.so.3.13.1[7fa20c8c3000+3c000]
This is due to the PCRE recursion. This is a known bug in PHP since 2008, but it's source is not PHP itself but PCRE library.
Rasmus Lerdorf has the answer: https://bugs.php.net/bug.php?id=45735#1365812629
"The problem here is that there is no way to detect run-away regular expressions
here without huge performance and memory penalties. Yes, we could build PCRE in a
way that it wouldn't segfault and we could crank up the default backtrack limit
to something huge, but it would slow every regex call down by a lot. If PCRE
provided a way to handle this in a more graceful manner without the performance
hit we would of course use it."
biziclop at vipmail dot hu ¶
8 months ago
Sometimes you don't just want to cherry-pick the matches but need that the entire subject is made up from matching substrings, so every character of the subject is a member of a match. None of the existing preg_* function is easily applicable for this task, so I made the preg_match_entire() function.
It uses the (*MARK) syntax which is documented here: https://pcre.org/original/doc/html/pcrepattern.html#SEC27
<?php // returns: the array of matches
// null if the string is not a repetition of the pattern
// false on error
function preg_match_entire( string $pattern, string $subject, int $flags = 0 ){
// Rebuild and wrap the pattern
$delimiter = $pattern[0];
$ldp = strrpos( $pattern, $delimiter );
$pattern = substr( $pattern, 1, $ldp - 1 );
$modifiers = substr( $pattern, $ldp + 1 );
$pattern = "{$delimiter} Gz (*MARK:END) | G (?:{$pattern}) {$delimiter}x{$modifiers}";
$r = preg_match_all( $pattern, $subject, $m, PREG_SET_ORDER | $flags );
if( $r === false ) return false; // error
$end = array_pop( $m );
if( $end === null || ! isset( $end['MARK']) || $end['MARK'] !== 'END')
return null; // end of string not reached
return $m; // return actual matches, may be an empty array
}// Same results:
test('#{d+}#', ''); // []
test('#{d+}#', '{11}{22}{33}'); // {11},{22},{33}
// Different results: preg_match_entire won't match this:
test('#{d+}#', '{11}{}{aa}{22},{{33}}');
// preg_match_entire: null
// preg_match_all: {11},{22},{33}function test( $pattern, $subject ){
echo "pattern: $patternn";
echo "subject: $subjectn";
print_matches('preg_match_entire: ', preg_match_entire( $pattern, $subject ));
preg_match_all( $pattern, $subject, $matches, PREG_SET_ORDER );
print_matches('preg_match_all: ', $matches );
echo "n";
}
function print_matches( $t, $m ){
echo $t, is_array( $m ) && $m ? implode(',', array_column( $m, 0 )) : json_encode( $m ), "n";
} ?>
rajudec at gmail dot com ¶
1 year ago
<?php
//Allow limited span formatting in html text$str='<span style="text-decoration-line: underline; font-weight: bold; font-style: italic;">White</span>
<span style="text-decoration-line: underline;">RED</span><span style="color:blue">blue</span>';
function
next_format($str)
{
$array=array("text-decoration-line"=>"underline","font-weight"=>"bold","font-style"=>"italic");
foreach ($array as $key=>$val)
{
if($str[1]==$key && $str[2]==$val)
{
return $str[1].': '.$str[2].";";
}
}
return '';
}
function
next_span($matches)
{
$needFormat=preg_replace_callback('/([a-z-]+):s*([^;]+)(;|)/ism',"next_format",$matches[2]);
return $matches[1].$needFormat.$matches[3];
}
echo
preg_replace_callback(
"/(<spans+style=")([^"]+)(">)/ism",
"next_span",
$str);
?>
mojo ¶
1 year ago
Why <?php preg_match_all('/(?:^|s)(ABC|XYZ)(?:s|$)/i', 'ABC XYZ', $match) ?> finds only 'ABC'?
Because the first full match is 'ABC ' - containing the trailing space. And that space is not available for further processing.
Use lookbehind and lookahead to solve this problem: <?php preg_match_all('/(?<=^|s)(ABC|XYZ)(?=s|$)/i', 'ABC XYZ', $match) ?>
chris at ocproducts dot com ¶
2 years ago
If PREG_OFFSET_CAPTURE is set then unmatched captures (i.e. ones with '?') will not be present in the result array. This is presumably because there is no offset, and thus the original PHP dev decided best to just leave it out.
fseverin at free dot fr ¶
10 years ago
As I intended to create for my own purpose a clean PHP class to act on XML files, combining the use of DOM and simplexml functions, I had that small problem, but very annoying, that the offsets in a path is not numbered the same in both.
That is to say, for example, if i get a DOM xpath object it appears like:
/ANODE/ANOTHERNODE/SOMENODE[9]/NODE[2]
and as a simplexml object would be equivalent to:
ANODE->ANOTHERNODE->SOMENODE[8]->NODE[1]
So u see what I mean? I used preg_match_all to solve that problem, and finally I got this after some hours of headlock (as I'm french the names of variables are in French sorry), hoping it could be useful to some of you:
<?php
function decrease_string($string)
{
/* retrieve all occurrences AND offsets of numbers in the original string: */
preg_match_all("/[0-9]+/",$chaine,$out,PREG_OFFSET_CAPTURE);
for($i=0;$i<sizeof($out[0]);$i++)
{
$longueurnombre = strlen((string)$out[0][$i][0]);
$taillechaine = strlen($chaine);
// cut the string in 3 pieces
$debut = substr($chaine,0,$out[0][$i][1]);
$milieu = ($out[0][$i][0])-1;
$fin = substr($chaine,$out[0][$i][1]+$longueurnombre,$taillechaine);
/* if it's 10,100,1000, the problem is that the string gets shorter and it shifts all the offsets, so we have to decrease them of 1 */
if(preg_match('#[1][0]+$#', $out[0][$i][0]))
{
for($j = $i+1;$j<sizeof($out[0]);$j++)
{
$out[0][$j][1] = $out[0][$j][1] -1;
}
}
$chaine = $debut.$milieu.$fin;
}
return $chaine;
}
?>
marc ¶
10 years ago
Better use preg_replace to convert text in a clickable link with tag <a>
$html = preg_replace('"b(http://S+)"', '<a href="$1">$1</a>', $text);
royaltm75 at gmail dot com ¶
13 years ago
I have received complains, that my html2a() code (see below) doesn't work in some cases.
It is however not the problem with algorithm or procedure, but with PCRE recursive stack limits.
If you use recursive PCRE (?R) you should remember to increase those two ini settings:
ini_set('pcre.backtrack_limit', 10000000);
ini_set('pcre.recursion_limit', 10000000);
But be warned: (from php.ini)
;Please note that if you set this value to a high number you may consume all
;the available process stack and eventually crash PHP (due to reaching the
;stack size limit imposed by the Operating System).
I have written this example mainly to demonstrate the power of PCRE LANGUAGE, not the power of it's implementation :)
But if you like it, use it, of course on your own risk.
elyknosrac at gmail dot com ¶
13 years ago
Using preg_match_all I made a pretty handy function.
<?php
function reg_smart_replace($pattern, $replacement, $subject, $replacementChar = "$$$", $limit = -1)
{
if (! $pattern || ! $subject || ! $replacement ) { return false; }
$replacementChar = preg_quote($replacementChar);
preg_match_all ( $pattern, $subject, $matches);
if (
$limit > -1) {
foreach ($matches as $count => $value )
{
if ($count + 1 > $limit ) { unset($matches[$count]); }
}
}
foreach ($matches[0] as $match) {
$rep = ereg_replace($replacementChar, $match, $replacement);
$subject = ereg_replace($match, $rep, $subject);
}
return
$subject;
}
?>
This function can turn blocks of text into clickable links or whatever. Example:
<?php
reg_smart_replace(EMAIL_REGEX, '<a href="mailto:$$$">$$$</a>', $description)
?>
will turn all email addresses into actual links.
Just substitute $$$ with the text that will be found by the regex. If you can't use $$$ then use the 4th parameter $replacementChar
MonkeyMan ¶
14 years ago
Here is a way to match everything on the page, performing an action for each match as you go. I had used this idiom in other languages, where its use is customary, but in PHP it seems to be not quite as common.
<?php
function custom_preg_match_all($pattern, $subject)
{
$offset = 0;
$match_count = 0;
while(preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE, $offset))
{
// Increment counter
$match_count++;// Get byte offset and byte length (assuming single byte encoded)
$match_start = $matches[0][1];
$match_length = strlen(matches[0][0]);// (Optional) Transform $matches to the format it is usually set as (without PREG_OFFSET_CAPTURE set)
foreach($matches as $k => $match) $newmatches[$k] = $match[0];
$matches = $new_matches;// Your code here
echo "Match number $match_count, at byte offset $match_start, $match_length bytes long: ".$matches[0]."rn";// Update offset to the end of the match
$offset = $match_start + $match_length;
}
return
$match_count;
}
?>
Note that the offsets returned are byte values (not necessarily number of characters) so you'll have to make sure the data is single-byte encoded. (Or have a look at paolo mosna's strByte function on the strlen manual page).
I'd be interested to know how this method performs speedwise against using preg_match_all and then recursing through the results.
dolbegraeb ¶
15 years ago
please note, that the function of "mail at SPAMBUSTER at milianw dot de" can result in invalid xhtml in some cases. think i used it in the right way but my result is sth like this:
<img src="./img.jpg" alt="nice picture" />foo foo foo foo </img>
correct me if i'm wrong.
i'll see when there's time to fix that. -.-
matt at lvl99 dot com ¶
7 years ago
I had been crafting and testing some regexp patterns online using the tools Regex101 and a `preg_match_all()` tester and found that the regexp patterns I wrote worked fine on them, just not in my code.
My problem was not double-escaping backslash characters:
<?php
// Input test
$input = ""something","something here","somennewnlines","this is the end"";// Work with online regexp testers, doesn't work in PHP
preg_match_all( "/(?:,|^)(?<!\)".*?(?<!\)"(?:(?=,)|$)/s", $input, $matches );/*
Outputs: NULL
*/
// Works with online regexp testers, does work in PHP
preg_match_all( "/(?:,|^)(?<!\\)".*?(?<!\\)"(?:(?=,)|$)/s", $input, $matches );/*
Outputs:
array(2) {
[0]=>
array(4) {
[0]=>
string(11) ""something""
[1]=>
string(17) ","something here""
[2]=>
string(17) ","some
new
lines""
[3]=>
string(18) ","this is the end""
}
[1]=>
array(4) {
[0]=>
string(9) "something"
[1]=>
string(14) "something here"
[2]=>
string(14) "some
new
lines"
[3]=>
string(15) "this is the end"
}
}
*/
?>
phektus at gmail dot com ¶
15 years ago
If you'd like to include DOUBLE QUOTES on a regular expression for use with preg_match_all, try ESCAPING THRICE, as in: \"
For example, the pattern:
'/<table>[sw/<>=\"]*</table>/'
Should be able to match:
<table>
<row>
<col align="left" valign="top">a</col>
<col align="right" valign="bottom">b</col>
</row>
</table>
.. with all there is under those table tags.
I'm not really sure why this is so, but I tried just the double quote and one or even two escape characters and it won't work. In my frustration I added another one and then it's cool.
ajeet dot nigam at icfaitechweb dot com ¶
9 years ago
Here http://tryphpregex.com/ is a php based online regex editor which helps you test your regular expressions with real-time highlighting of regex match on data input.
vojjov dot artem at ya dot ru ¶
7 years ago
// Here is function that allows you to preg_match_all array of patters
function getMatches($pattern, $subject) {
$matches = array();
if (is_array($pattern)) {
foreach ($pattern as $p) {
$m = getMatches($p, $subject);
foreach ($m as $key => $match) {
if (isset($matches[$key])) {
$matches[$key] = array_merge($matches[$key], $m[$key]);
} else {
$matches[$key] = $m[$key];
}
}
}
} else {
preg_match_all($pattern, $subject, $matches);
}
return $matches;
}
$patterns = array(
'/<span>(.*?)</span>/',
'/<a href=".*?">(.*?)</a>/'
);
$html = '<span>some text</span>';
$html .= '<span>some text in another span</span>';
$html .= '<a href="path/">here is the link</a>';
$html .= '<address>address is here</address>';
$html .= '<span>here is one more span</span>';
$matches = getMatches($patterns, $html);
print_r($matches); // result is below
/*
Array
(
[0] => Array
(
[0] => <span>some text</span>
[1] => <span>some text in another span</span>
[2] => <span>here is one more span</span>
[3] => <a href="path/">here is the link</a>
)
[1] => Array
(
[0] => some text
[1] => some text in another span
[2] => here is one more span
[3] => here is the link
)
)
*/
DarkSide ¶
8 years ago
This is very useful to combine matches:
$a = array_combine($matches[1], $matches[2]);
satyavvd at ymail dot com ¶
11 years ago
Extract fields out of csv string : ( since before php5.3 you can't use str_getcsv function )
Here is the regex :
<?php
$csvData
= <<<EOF
10,'20',"30","'40","'50'",""60","70,80","09\/18,/"2011",'a,sdfcd'
EOF
$reg = <<<EOF
/
(
(
(['"])
(
(
[^'"]
|
(\\.)
)*
)
(\3)
|
(
[^,]
|
(\\.)
)*
),)
/x
EOF;
preg_match_all($reg,$csvData,$matches);
// to extract csv fields
print_r($matches[2]);
?>
royaltm75 at NOSPAM dot gmail dot com ¶
14 years ago
The power of pregs is limited only by your *imagination* :)
I wrote this html2a() function using preg recursive match (?R) which provides quite safe and bulletproof html/xml extraction:
<?php
function html2a ( $html ) {
if ( !preg_match_all( '
@
<s*?(w+)((?:b(?:'[^']*'|"[^"]*"|[^>])*)?)>
((?:(?>[^<]*)|(?R))*)
</s*?\1(?:b[^>]*)?>
|<s*(w+)(b(?:'[^']*'|"[^"]*"|[^>])*)?/?>
@uxis', $html = trim($html), $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER) )
return $html;
$i = 0;
$ret = array();
foreach ($m as $set) {
if ( strlen( $val = trim( substr($html, $i, $set[0][1] - $i) ) ) )
$ret[] = $val;
$val = $set[1][1] < 0
? array( 'tag' => strtolower($set[4][0]) )
: array( 'tag' => strtolower($set[1][0]), 'val' => html2a($set[3][0]) );
if ( preg_match_all( '
/(w+)s*(?:=s*(?:"([^"]*)"|'([^']*)'|(w+)))?/usix
', isset($set[5]) && $set[2][1] < 0
? $set[5][0]
: $set[2][0]
,$attrs, PREG_SET_ORDER ) ) {
foreach ($attrs as $a) {
$val['attr'][$a[1]]=$a[count($a)-1];
}
}
$ret[] = $val;
$i = $set[0][1]+strlen( $set[0][0] );
}
$l = strlen($html);
if ( $i < $l )
if ( strlen( $val = trim( substr( $html, $i, $l - $i ) ) ) )
$ret[] = $val;
return $ret;
}
?>
Now let's try it with this example: (there are some really nasty xhtml compliant bugs, but ... we shouldn't worry)
<?php
$html = <<<EOT
some leftover text...
< DIV class=noCompliant style = "text-align:left;" >
... and some other ...
< dIv > < empty> </ empty>
<p> This is yet another text <br >
that wasn't <b>compliant</b> too... <br />
</p>
<div class="noClass" > this one is better but we don't care anyway </div ><P>
<input type= "text" name ='my "name' value = "nothin really." readonly>
end of paragraph </p> </Div> </div> some trailing text
EOT;$a = html2a($html);
//now we will make some neat html out of it
echo a2html($a);
function
a2html ( $a, $in = "" ) {
if ( is_array($a) ) {
$s = "";
foreach ($a as $t)
if ( is_array($t) ) {
$attrs="";
if ( isset($t['attr']) )
foreach( $t['attr'] as $k => $v )
$attrs.=" ${k}=".( strpos( $v, '"' )!==false ? "'$v'" : ""$v"" );
$s.= $in."<".$t['tag'].$attrs.( isset( $t['val'] ) ? ">n".a2html( $t['val'], $in." " ).$in."</".$t['tag'] : "/" ).">n";
} else
$s.= $in.$t."n";
} else {
$s = empty($a) ? "" : $in.$a."n";
}
return $s;
}
?>
This produces:
some leftover text...
<div class="noCompliant" style="text-align:left;">
... and some other ...
<div>
<empty>
</empty>
<p>
This is yet another text
<br/>
that wasn't
<b>
compliant
</b>
too...
<br/>
</p>
<div class="noClass">
this one is better but we don't care anyway
</div>
<p>
<input type="text" name='my "name' value="nothin really." readonly="readonly"/>
end of paragraph
</p>
</div>
</div>
some trailing text
mr davin ¶
15 years ago
<?php
// Returns an array of strings where the start and end are found
function findinside($start, $end, $string) {
preg_match_all('/' . preg_quote($start, '/') . '([^.)]+)'. preg_quote($end, '/').'/i', $string, $m);
return $m[1];
}$start = "mary has";
$end = "lambs.";
$string = "mary has 6 lambs. phil has 13 lambs. mary stole phil's lambs. now mary has all the lambs.";$out = findinside($start, $end, $string);print_r ($out);/* Results in
(
[0] => 6
[1] => all the
)
*/
?>
avengis at gmail dot com ¶
13 years ago
The next function works with almost any complex xml/xhtml string
<?php
/**
* Find and close unclosed xml tags
**/
function close_tags($text) {
$patt_open = "%((?<!</)(?<=<)[s]*[^/!>s]+(?=>|[s]+[^>]*[^/]>)(?!/>))%";
$patt_close = "%((?<=</)([^>]+)(?=>))%";
if (preg_match_all($patt_open,$text,$matches))
{
$m_open = $matches[1];
if(!empty($m_open))
{
preg_match_all($patt_close,$text,$matches2);
$m_close = $matches2[1];
if (count($m_open) > count($m_close))
{
$m_open = array_reverse($m_open);
foreach ($m_close as $tag) $c_tags[$tag]++;
foreach ($m_open as $k => $tag) if ($c_tags[$tag]--<=0) $text.='</'.$tag.'>';
}
}
}
return $text;
}
?>
I’m quite new to regular expressions. Could you help me to create a pattern, which matches whole words, containing specific part? For example, if I have a text string «Perform a regular expression match» and if I search for express, it shuld give me expression, if I search for form, it should give me Perform and so on. Got the idea?
asked Jan 18, 2011 at 8:41
preg_match('/b(expressw+)b/', $string, $matches); // matches expression
preg_match('/b(w*formw*)b/', $string, $matches); // matches perform,
// formation, unformatted
Where:
b
is a word boundaryw+
is one or more «word» character*w*
is zero or more «word» characters
See the manual on escape sequences for PCRE.
* Note: although not really a «word character», the underscore _
is also included int the character class w
.
answered Jan 18, 2011 at 8:44
Linus KleenLinus Kleen
33.6k11 gold badges90 silver badges99 bronze badges
5
This matches ‘Perform’:
b(w*formw*)b
This matches ‘expression’:
b(w*expressw*)b
answered Jan 18, 2011 at 8:51
Kyle WildKyle Wild
8,8052 gold badges36 silver badges36 bronze badges
За последние 24 часа нас посетили 13692 программиста и 1029 роботов. Сейчас ищет 471 программист …
Вернуться к: PCRE
preg_match_all
(PHP 4, PHP 5, PHP 7)
preg_match_all — Выполняет глобальный поиск шаблона в строке
Описание
int preg_match_all
( string $pattern
, string $subject
[, array &$matches
[, int $flags
= PREG_PATTERN_ORDER
[, int $offset
= 0
]]] )
После нахождения первого соответствия последующие поиски будут осуществляться
не с начала строки, а от конца последнего найденного вхождения.
Список параметров
-
pattern
-
Искомый шаблон, строка.
-
subject
-
Входная строка.
-
matches
-
Параметр
flags
регулирует порядок вывода
совпадений в возвращаемом многомерном массиве. -
flags
-
Может быть комбинацией следующих флагов (необходимо понимать,
что использованиеPREG_PATTERN_ORDER
одновременно сPREG_SET_ORDER
бессмысленно):-
PREG_PATTERN_ORDER
-
Упорядочивает результаты так, что элемент $matches[0]
содержит массив полных вхождений шаблона, элемент $matches[1]
содержит массив вхождений первой подмаски, и так далее.
<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
"<b>пример: </b><div align=left>это тест</div>",
$out, PREG_PATTERN_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "n";
echo $out[1][0] . ", " . $out[1][1] . "n";
?>
Результат выполнения данного примера:
<b>пример: </b>, <div align=left>это тест</div> пример: , это тест
Таким образом, $out[0] содержит массив полных вхождений шаблона,
а $out[1] содержит массив подстрок, содержащихся в тегах. -
PREG_SET_ORDER
-
Упорядочивает результаты так, что элемент $matches[0] содержит первый
набор вхождений, элемент $matches[1] содержит второй набор вхождений,
и так далее.
<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
"<b>пример: </b><div align="left">это тест</div>",
$out, PREG_SET_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "n";
echo $out[1][0] . ", " . $out[1][1] . "n";
?>
Результат выполнения данного примера:
<b>пример: </b>, пример: <div align="left">это тест</div>, это тест
-
PREG_OFFSET_CAPTURE
-
В случае, если этот флаг указан, для каждой найденной подстроки будет указана
ее позиция в исходной строке. Необходимо помнить, что этот флаг меняет формат
возвращаемого массиваmatches
в массив, каждый элемент
которого содержит массив, содержащий в индексе с номером 0
найденную подстроку, а смещение этой подстроки в параметреsubject
— в индексе 1.
В случае если флаги не были указаны, по умолчанию используется
PREG_PATTERN_ORDER
. -
-
offset
-
Обычно поиск осуществляется слева направо, с начала строки. Дополнительный параметр
offset
может быть использован для указания альтернативной
начальной позиции для поиска.Замечание:
Использование параметра
offset
не эквивалентно
замене сопоставляемой строки выражением substr($subject, $offset)
при вызове функции preg_match_all(), поскольку
шаблонpattern
может содержать такие условия как
^, $ или (?<=x).
Вы можете найти соответствующие примеры в описании функции
preg_match().
Возвращаемые значения
Возвращает количество найденных вхождений шаблона (которое может
быть и нулем) либо FALSE
, если во время выполнения возникли какие-либо ошибки.
Список изменений
Версия | Описание |
---|---|
5.4.0 |
Параметр matches стал необязательным.
|
5.3.6 |
Возвращает FALSE если offset больше чем длина subject .
|
5.2.2 |
Именованные подмаски теперь принимают синтаксис (?<name>) и (?’name’), также как и (?P<name>). Предыдущие версии принимали только (?P<name>). |
Примеры
Пример #1 Получение всех телефонных номеров из текста.
<?php
preg_match_all("/(? (d{3})? )? (?(1) [-s] ) d{3}-d{4}/x",
"Звоните 555-1212 или 1-800-555-1212", $phones);
?>
Пример #2 Жадный поиск совпадений с HTML-тэгами
<?php
// Запись \2 является примером использования ссылок на подмаски.
// Она означает необходимость соответствия подстроки строке, захваченной
// второй подмаской, в нашем примере это ([w]+).
// Дополнительный обратный слеш необходим, так как используются двойные кавычки.
$html = "<b>полужирный текст</b><a href=howdy.html>нажми</a>";preg_match_all("/(<([w]+)[^>]*>)(.*?)(</\2>)/", $html, $matches, PREG_SET_ORDER);
foreach (
$matches as $val) {
echo "совпадение: " . $val[0] . "n";
echo "часть 1: " . $val[1] . "n";
echo "часть 2: " . $val[2] . "n";
echo "часть 3: " . $val[3] . "n";
echo "часть 4: " . $val[4] . "nn";
}
?>
Результат выполнения данного примера:
совпадение: <b>полужирный текст</b> часть 1: <b> часть 2: b часть 3: полужирный текст часть 4: </b> matched: <a href=hody.html>нажми</a> часть 1: <a href=howdy.html> часть 2: a часть 3: нажми часть 4: </a>
Пример #3 Использование именованных подмасок
<?php
$str
= <<<FOO
a: 1
b: 2
c: 3
FOO;preg_match_all('/(?P<name>w+): (?P<digit>d+)/', $str, $matches);/* Это также работает в PHP 5.2.2 (PCRE 7.0) и более поздних версиях,
* однако, вышеуказанная форма рекомендуется для обратной совместимости */
// preg_match_all('/(?<name>w+): (?<digit>d+)/', $str, $matches);print_r($matches);?>
Результат выполнения данного примера:
Array ( [0] => Array ( [0] => a: 1 [1] => b: 2 [2] => c: 3 ) [name] => Array ( [0] => a [1] => b [2] => c ) [1] => Array ( [0] => a [1] => b [2] => c ) [digit] => Array ( [0] => 1 [1] => 2 [2] => 3 ) [2] => Array ( [0] => 1 [1] => 2 [2] => 3 ) )
Смотрите также
- «Регулярные выражения PCRE»
- preg_quote() — Экранирует символы в регулярных выражениях
- preg_match() — Выполняет проверку на соответствие регулярному выражению
- preg_replace() — Выполняет поиск и замену по регулярному выражению
- preg_split() — Разбивает строку по регулярному выражению
- preg_last_error() — Возвращает код ошибки выполнения последнего регулярного выражения PCRE
Вернуться к: PCRE
Summary: in this tutorial, you’ll learn how to use the PHP preg_match_all()
function to search for all matches to a regular expression in a string.
Introduction to the PHP preg_match_all() function
The preg_match_all()
function searches for all the matches to a regular expression in a string.
Unlike the preg_match()
function that stops searching when it finds the first match, the preg_match_all()
function continues searching for the next matches till the end of the string.
The following shows the syntax of the preg_match_all()
function:
preg_match_all(
string $pattern,
string $subject,
array &$matches = null,
int $flags = 0,
int $offset = 0
): int|false|null
Code language: PHP (php)
The preg_match_all()
function accepts the following parameters:
$pattern
is a string that specifies the pattern to search.$subject
is the input string to match the pattern.$matches
is a multi-dimensional array that contains all the matches.$flags
is a combination of the flagsPREG_PATTERN_ORDER
,PREG_SET_ORDER
,PREG_OFFSET_CAPTURE
, andPREG_UNMATCHED_AS_NULL
. By default, the$flags
isPREG_PATTERN_ORDER
if you skip it.$offset
is an integer that specifies the position from which the search starts. By default, thepreg_match_all()
function starts searching from the beginning of the string.
The preg_match_all()
function returns a number that specifies the number of full pattern matches. If there is no match, the preg_match_all()
function returns zero. In case of failure, it returns false
.
Let’s take some examples of using the preg_match_all()
function.
1) Using the PHP preg_match_all() function to match numbers in a string example
The following example uses the preg_match_all()
function to search for all numbers with one or more digits in a string:
<?php
$pattern = '/d+/';
$str = 'PHP 1.0 released in 1995';
if (preg_match_all($pattern, $str, $matches)) {
print_r($matches);
}
Code language: PHP (php)
Output:
Array
(
[0] => Array
(
[0] => 1
[1] => 0
[2] => 1995
)
)
Code language: PHP (php)
It returns three matches 0
, 1
, and 1995
. If you use the preg_match()
function, it’ll return the first number (1
) only.
2) Using the preg_match_all() function with flags parameters
The following example uses the preg_match_all()
function to match the word in a string. It also captures the first character of each word:
<?php
$pattern = '/b([a-zA-Z])w+b/';
$str = 'Alice, Bob, Peter';
if (preg_match_all($pattern, $str, $matches)) {
print_r($matches);
}
Code language: PHP (php)
Output:
Array
(
[0] => Array
(
[0] => Alice
[1] => Bob
[2] => Peter
)
[1] => Array
(
[0] => A
[1] => B
[2] => P
)
)
Code language: PHP (php)
The $matche
s array contains the full pattern matches in the first element and the capturing groups in the second element. It returns the same result as if you use the PREG_PATTERN_ORDER
flag.
If you want to group each set of matches in an array element, you can use the PREG_SET_ORDER
flag.
The PREG_SET_ORDER
flag groups the first set of matches in the $matches[0]
, the second est of matches in the $matches[1]
, and so on. For example:
<?php
$pattern = '/b([a-zA-Z])w+b/';
$str = 'Alice, Bob, Peter';
if (preg_match_all($pattern, $str, $matches, PREG_SET_ORDER)) {
print_r($matches);
}
Code language: PHP (php)
Output:
Array
(
[0] => Array
(
[0] => Alice
[1] => A
)
[1] => Array
(
[0] => Bob
[1] => B
)
[2] => Array
(
[0] => Peter
[1] => P
)
)
Code language: PHP (php)
The $flags
can also be:
PREG_OFFSET_CAPTURE
returns the offset of the match together with the matched string.PREG_UNMATCHED_AS_NULL
returnsNULL
instead of an empty string if no match is found for the subpatterns a.k.a capturing groups.
To combine flags, you place the |
operator between two of them. For example:
<?php
$pattern = '/b([a-zA-Z])w+b/';
$str = 'Alice, Bob, Peter';
if (preg_match_all($pattern, $str, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
print_r($matches);
}
Code language: PHP (php)
Output:
Array
(
[0] => Array
(
[0] => Array
(
[0] => Alice
[1] => 0
)
[1] => Array
(
[0] => A
[1] => 0
)
)
[1] => Array
(
[0] => Array
(
[0] => Bob
[1] => 7
)
[1] => Array
(
[0] => B
[1] => 7
)
)
[2] => Array
(
[0] => Array
(
[0] => Peter
[1] => 12
)
[1] => Array
(
[0] => P
[1] => 12
)
)
)
Code language: PHP (php)
Note that it doesn’t make sense to combine PREG_PATTERN_ORDER
and PREG_SET_ORDER
flags.
Summary
- Use the PHP
preg_match_all()
function to search for all matches in a string.
Did you find this tutorial useful?
Время на прочтение
9 мин
Количество просмотров 96K
В преддверии старта нового потока по курсу «Backend-разработчик на PHP», а также смежного с ним курса «Framework Laravel», хотим поделиться статьей, которую подготовил наш внештатный автор.
Внимание! данная статья не имеет отношения к программе курса и будет полезна только для новичков. Для получения более углубленных знаний приглашаем вас посетить бесплатный двухдневный онлайн интенсив по теме: «Создание Telegram-бота для заказа кофе в заведении и оплаты онлайн». Второй день интенсива будет проходить тут.
Всем привет! Всех с наступившим [20]{2,}0
годом. Сегодня я хочу затронуть тему, которая иногда является темой для шуток от «Да зачем тебе все это учить, если есть уже есть готовые решения» до «может тебе еще и весь Perl выучить?». Однако время идет, множество программистов начинают осваивать регулярные выражения, а на Хабре нет ни одной свежей (хоть регулярные выражения не слишком изменились за последнее время) статьи на этой тематику. Пришло время написать ещё одну!
Регулярные выражения в отрыве от их конкретной реализации
Регулярные выражения (обозначаемые в английском как RegEx или как regex) являются инструментальным средством, которое применяется для различных вариантов изучения и обработки текста: поиска, проверки, поиска и замены того или иного элемента, состоящего из букв или цифр (или любых других символов, в том числе специальных символов и символов пунктуации). Изначально регулярные выражения пришли в мир программирования из среды научных исследований, которые проводились в 50-е годы в области математики.
Спустя десятилетия принципы и идеи были перенесены в среду операционной системы UNIX (в частности вошли в утилиту grep
) и были реализованы в языке программирования Perl, который на заре интернета широко использовался на бэкенде (и по сей день используется, но уже меньше) для такой задачи, как, например, валидация форм.
Если они вроде простые, тогда почему такие страшные на первый взгляд?
На самом деле любое выражение может быть «регулярным» и применяться для проверки или поиска каких-либо символов. Например, слова Pavel или example@mail.ru тоже могут использоваться как регулярки, только, понятное дело, в довольно узком ключе. Для проверки работы регулярных выражений в среде PHP без запуска своего сервера или хостинга вы можете воспользоваться следующим онлайн сервисом (вот только на нем у меня не работала обработка русских символов). Для начала в качестве регулярного выражения мы используем просто Pavel.
Положим у нас есть следующий текст:
Pavel knows too much. Pavel using nginx and he’s not rambler.
Сейчас регулярные выражения нашли оба вхождения слова Pavel. Здорово, но звучит не очень полезно (разве что только вы зачем-то пытаетесь проанализировать что-то вроде количества упоминания слова сударь в Войне и Мире через Vim и Python, но тогда у меня к вам вопросов нет).
Вариативность выражения
Если ваше регулярное выражение вариативно (например, вам известна только некоторая его часть и нужно найти количество вхождений годов, начиная от 2000 и заканчивая 2099), то мы можем использовать следующее регулярное выражение: 20..
Текст: Молодые писатели пишут много чего. Например писатель 2002 года рождения очень отличается от 2008 и 2012
Здесь у нас с помощью регулярного выражения найдутся все годы, но пока в этом нет никакого смысла. Скорее всего нам не нужны годы дальше 2012 (хотя молодые писатели младше 8 лет могут обидеться, но не об этом сейчас). Стоит изучить наборы символов, но об этом попозже, потому как сейчас поговорим про другую важную часть регулярных выражений: экранирование метасимволов.
Представим, что нам нужно найти количество вхождений файлов с расширением .doc
(допустим, мы экспортируем только определенные файлы загруженные в нашу базу данных). Но ведь точка обозначает просто любой символ? Так как же быть?
Тут к нам на помощь приходит экранирование метасимволов обратным слешем . Теперь выражение
.doc
будет достаточно успешно искать любой текстовое упоминание с расширением .doc
:
Регулярное выражение: .doc
Текст: kursach.doc , nepodozritelneyfail.exe
, work.doc, shaprgalka.rtf doc
Как видите, мы успешно можем найти количество файлов с расширением .doc
в списке. Однако мы не сможем вытащить полные имена файлов с помощью данного регулярного выражения, например, в массив. Пришло время взглянуть на наборы символов.
Совпадение с целым набором символов
В регулярных выражениях совпадения с набором обеспечивается с помощью метасимволов — квадратных скобочек [ ]
. Любые два символа ASII могут быть указаны в качестве начала и конца диапазона. Для простой реализации, положим, мы хотим найти все пронумерованные файлы от 0 до 9 с расширением .jpg
.
Регулярное выражение: [0-9].jpg
Текст: 1.jpg, 2.jpg, 3.jpg, photo.jpg, anime.jpg, 8.jpg, jkl.jpg
Стоит отметить, что имя файлов из более 1 цифры наше регулярное выражение не охватит. Про множественный выбор будет чуть ниже, а пока представим, что нам вдруг понадобилось добиться обратного результата. Добавим метасимвол ^
(у которого, самое противное, в регулярных выражениях есть аж две функции). Чтобы воспользоваться им как исключением, нужно добавить его именно внутрь нашего набора:
Регулярное выражение: [^0-9].jpg
Текст: 1.jpg, 2.jpg, 3.jpg, photo.jpg , anime.jpg , 8.jpg, jkl.jpg
Но без множественного выбора это конечно неполноценные выражения.
Полезные таблицы
Приведем таблицу метасимволов:
Таблица пробельных метасимволов
Множественный выбор: делаем простую валидацию
Вооружившись полученными знаниями, попробуем сделать регулярное выражение, которое находит, например, слова короче 3 букв (стандартная задача для антиспама). Если мы попробуем использовать следующее регулярное выражение — w{1,3}
(в котором метасимвол w
указывает на любой символ, а фигурные скобки обозначают количество символов от сколько до скольки, то у нас выделятся все символы подряд — нужно как-то обозначить начало и конец слов в тексте. Для этого нам потребуется метасимвол b
.
Регулярное выражение: bw{1,3}b:
Текст: good word
not
egg
Неплохо! Теперь слова короче трех букв не смогут попадать в нашу базу данных. Посмотрим на валидацию почтового адреса:
Регулярное выражение: w+@w+.w+
Требования: в электронной почте в начале должен быть любой символ (цифры или буквы, ведь электронная почта, которая состоит только из цифр в начале, встречается довольно часто). Потом идет символ @
, затем — сколько угодно символов, после чего экранированная точка (т.е. просто точка) и домен первого уровня.
Подробнее рассмотрим повторение символов
Теперь давайте поподробнее разберем, как можно в регулярных выражениях задать повторение символов. К примеру вы хотите найти любые комбинации цифр от 2-6 в тексте:
Регулярное выражение: [2-6]+
Текст: Here are come’s 89 different 234 digits 24 .
Давайте я приведу таблицу всех квантификаторов метасимволов:
В применении квантификаторов нет ничего сложного. Кроме одного нюанса: жадные и ленивые квантификаторы. Приведем таблицу:
Ленивые квантификаторы отличаются от жадных тем, что они выхватывают минимальное, а не максимальное количество символов. Представим, что есть у нас задача найти все теги заголовков h1-h6 и их контент, а весь остальной текст не должен быть затронут (я умышленно ввел несуществующий тэг h7, чтобы не мучаться с экранированием хабровских тэгов):
Регулярное выражение: <h[1-7]>.*?</h[1-7]>
Текст: <
h7
>
hello </
h7
>
lorem ipsum avada kedavra <
h7
> buy<
/h7
>
Все сработало успешно, однако только благодаря ленивому квантификатору. В случае применения жадного квантификатора у нас выделился бы весь текст между тегами (полагаю, в иллюстрации это не нуждается).
Границы символьных строк
Границы символьных строк мы уже использовали выше. Приведем здесь более подробную таблицу:
Работа с подвыражениями
Подвыражения в регулярных выражениях делаются с помощью метасимвола группировки ()
.
Приведем пример регулярного выражения, которое универсально может находить различные вариации IP — адресов.
Регулярное выражение: (((25[0-5])|(2[0-4]d)|(1d{2})|(d{1,2})).){3}(((25[0-5]|(2[0-4]d)|(1d{2})|(d{1,2}))))
Текст: 255.255.255.255 просто адрес
191.198.174.192 wikipedia
87.240.190.67 vk
31.13.72.36 facebook
Здесь используется логический оператор |
(или), который позволяет нам составить регулярное выражение, которое соответствует правилу, по которому составляются IP- адреса. В IP адресе должно быть от 1 и до 3 цифр, в котором число из трех чисел может начинаться с 1, с 2 (или тогда вторая цифра должна быть в пределах от 0 и до 4), или начинаться с 25, и тогда 3 цифра оказывается в пределах от 0 и до 5. Также между каждой комбинацией цифр должна стоять точка. Используя приведенные выше таблицы, постарайтесь сами расшифровать регулярное выражение сверху. Регулярные выражения в начале пугают своей длинной, но длинные не значит сложные.
Просмотр вперед
Для просмотра выражения на любую комбинацию определенных символов указывается шаблон по которому обнаруживается, но не возвращается, совпадение. По существу, просмотр вперед определяет подвыражение и поэтому он формируется соответствующим образом. Синтаксический шаблон для просмотра вперед состоит из подвыражения, перед которым стоит ?=, а после равно следует сопоставляемый текст.
Приведем конкретную задачу: есть пароль, который должен состоят не менее чем из 7 символов и должен обязательно включать как минимум одну заглавную букву и цифру. Здесь все будет несколько сложнее, потому как пользователь должен иметь возможность поставить заглавную букву как в начале, так и в середине предложения (и тоже самое должно повторяться с буквой).
Следовательно, нам потребуется просмотр выражения вперед. Кроме того, нам нужно разбить на группы знаки. И я хочу ограничить его размеры от 8 и до 22 знаков:
Регулярное выражение: /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]{8,}$/
Текст: Qwerty123
Im789098
weakpassword
Особенности работы регулярных выражений именно в PHP
Для изучения работы регулярных выражений в PHP, изучите функции в официальной документации PCRE (Perl Compatible Regular Expressions) которая доступна на официальном сайте. Выражение должно быть заключено в разделители, например, в прямые слеши.
Разделителем могут выступать произвольные символы, кроме буквенно-цифровых, обратного слеша » и нулевого байта. Если символ разделителя встречается в шаблоне, его необходимо экранировать . В качестве разделителей доступны комбинации, пришедшие из Perl: (), {}, [].
Какие функции используются в php? В пакете PCRE предоставляются следующие функции для поддержки регулярных выражений:
- preg_grep() — выполняет поиск и возвращает массив совпадений.
- preg_match() — выполняет поиск первого совпадения с помощью регулярных выражений
- preg_match_all() — выполняет глобальный поиск с помощью регулярных выражений
- preg_quote() — принимает шаблон и возвращает его экранированную версию
- preg_replace() — выполняет операцию поиска и замены
- preg_replace_callback() — тоже выполняет операцию поиска и замены, но используют callback – функцию для любой конкретной замены
- preg_split() — разбивает символьную строку на подстроки
Для организации совпадения без учета регистра букв служит модификатор i
.
С помощью модификатора m
можно активировать режим обработки многострочного текста.
Замещающие строки допускается вычислять в виде кода PHP. Для активизации данного режима служит модификатор e.
Во всех функциях preg_replace()
, preg_replace_callback()
и preg_split()
поддерживается дополнительный аргумент, который вводит ограничения на максимальное количество замен или разбиений.
Обратные ссылки могут обозначаться с помощью знака $ (например $1), а в более ранних версиях вместо знака $ применяются знаки \.
Метасимволы E, l, L, u и U не используются (поэтому они и не были упомянуты в этой статье).
Наша статья была бы неполной без классов символов POSIX, которые также работают в PHP (и в общем вполне могут повысить читабельность ваших регулярок, но не все их спешат учить, потому как часто ломают логику выражения).
Под конец приведу пример конкретной реализации регулярных выражений в PHP, используя упомянутые выше реализации. Также я добавил валидацию имени пользователя, чтобы он не смог вводить слишком короткие сочетания букв (ну, положим, это никнеймы, а не имена, имена бывают короче двух букв):
$pattern_name = '/w{3,}/';
$pattern_mail = '/w+@w+.w+/';
$pattern_password = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]{8,}$/';
if (preg_match($pattern_name, $name) &&
preg_match($pattern_mail, $mail) &&
preg_match($pattern_password, $_POST['password'])) {
# тут происходит, к примеру, регистрация нового пользователя, отправка ему письма, и внесение в базу данных
}
Всем спасибо за внимание! Конечно, сегодня мы затронули только часть регулярных выражений и о них можно написать ещё несколько статей. К примеру, мы не поговорили о реализации поиска повторений одинаковых слов в тексте. Но я надеюсь, что полученных знаний хватит, чтобы осмысленно написать свою первую валидацию формы и уже потом перейти к более зубодробительным вещам.
По традиции, несколько полезных ссылок:
Шпаргалка от MIT по регулярным выражениям
Официальная часть документации php по регулярным выражениям.
На этом все. До встречи на интенсиве!
Второй день интенсива пройдет тут
What is a Regular Expression?
The regular expression is basically started from mathematics which defines a range of characters for some purpose. Different languages get references from this mathematical concept and applied it for searching patterns and validation of data. In this article, you will learn regex in PHP.
There are many use cases of regex in PHP like searching, validating, etc. In this article, you will learn about regular expressions in search patterns. Once you will be able to understand the regex, you can use it anywhere in PHP.
A regular expression can be used to search text from strings or to replace specific characters or strings.
A regular expression can be a single character or combination of some complex patterns.
Syntax of regular expression in PHP
The delimiter can be any character that is not a letter, number, backslash, or space. The most common delimiter is the forward-slash (/), but when your pattern contains forward slashes it is convenient to choose other delimiters such as # or ~.
Generally, regular expression in PHP consists of three parts.
- Delimiter – It specifies that tells the beginning or end of the string in the pattern. A delimiter cannot be a backslash, number, letter, or space. However, a forward slash can be used as a delimiter. If the patterns you are searching for consist of forwarding slashes, you can change the delimiter to tilt ~ or hash #.
- Pattern – It specified the pattern or sequence of characters to search.
- Modifiers (optional) – You can make the matching of pattern cases insensitive or sensitive.
Example
$exp = "/php.org/i";
RegextFunctions in PHP
PHP provides built-in functions for regular expressions. The most common regex functions in PHP are:
- Preg_replace() – It counts the number of times the pattern occurs in the string and return the count.
- Preg_match() – returns 0 in case of pattern not found in the string. 1 in case of found.
- Preg_match_all() – This function replaces the string with another string where the pattern is matched. You will get a more clear idea in the next section if you are finding it hard to understand.
preg_match function in PHP
Preg_match function returns the binary result. That is either 0 or 1. If the string contains the pattern you specify in the first argument of the preg_match function, it will return 1. 0 in case of not found.
Look at the following example, in which we search the string for characters PHP.
<?php
$str = "Visit php.org";
$pattern = "/php/i";
echo preg_match($pattern, $str); // Outputs 1
?>
preg_match_all function in PHP
This function does not tell if the pattern exists in the string or not. It returns the count of the number of times a pattern appears in the string. Look at the following example in which we have performed a case-insensitive search of patterns ing in the given string.
Example
<?php
$str = "It is raining heavily. Cars are slipping outside. ";
$pattern = "/ing/i";
echo preg_match_all($pattern, $str); // Outputs 4
?>
/i in the above example specify that the string can be either in lower case or upper case.
preg_replace function in PHP
preg_replace function returns a new string by replacing the pattern found in the string with some other string of characters. This function accepts three parameters. The first parameter is the pattern to search in the string in the second parameter. Finally, the third parameter is the string to put in place of the pattern found in the string.
Look at the following example, in which we replace the word website with PHP.org
Example
<?php
$str = "Visit Website!";
$pattern = "/Website/i";
echo preg_replace($pattern, "php.org", $str); // Outputs "Visit php.org!"
?>
Regular Expression Modifiers
You can adjust the search behavior of the regex in PHP by defining the following modifier variables.
- I – Used to perform case-insensitive searching.
- U – For the correct searching of utf-8 patterns.
- M – Adds multi-searching in the PHP regex. There are some patterns that search at the beginning or end of each line. So, these patterns look at the beginning and end of each line of the string.
Specify range in PHP regex
Understand the following three concepts. It will help you out to put the range in patterns while searching text or replacing it.
- [abc] – The square brackets indicated that find any one character from these characters in the string.
- [^0-9] – Look for the characters not in the range within the brackets.
- [0-9] – Find any one character in between 0 to 9. 0 and 9 are inclusive in this case.
You can learn more about Regex functions in PHP here
preg_match_all
(PHP 4, PHP 5, PHP 7, PHP
preg_match_all — Perform a global regular expression match
Description
preg_match_all( string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0 ): int|false
Searches subject
for all matches to the regular expression given in pattern
and puts them in matches
in the order specified by flags
.
After the first match is found, the subsequent searches are continued on from end of the last match.
Parameters
-
pattern
-
The pattern to search for, as a string.
-
subject
-
The input string.
-
matches
-
Array of all matches in multi-dimensional array ordered according to
flags
. -
flags
-
Can be a combination of the following flags (note that it doesn’t make sense to use
PREG_PATTERN_ORDER
together withPREG_SET_ORDER
):-
PREG_PATTERN_ORDER
-
Orders results so that $matches[0] is an array of full pattern matches, $matches[1] is an array of strings matched by the first parenthesized subpattern, and so on.
<?php preg_match_all("|<[^>]+>(.*)</[^>]+>|U", "<b>example: </b><div align=left>this is a test</div>", $out, PREG_PATTERN_ORDER); echo $out[0][0] . ", " . $out[0][1] . "n"; echo $out[1][0] . ", " . $out[1][1] . "n"; ?>
The above example will output:
<b>example: </b>, <div align=left>this is a test</div> example: , this is a test
So, $out[0] contains array of strings that matched full pattern, and $out[1] contains array of strings enclosed by tags.
If the pattern contains named subpatterns, $matches additionally contains entries for keys with the subpattern name.
If the pattern contains duplicate named subpatterns, only the rightmost subpattern is stored in $matches[NAME].
<?php preg_match_all( '/(?J)(?<match>foo)|(?<match>bar)/', 'foo bar', $matches, PREG_PATTERN_ORDER ); print_r($matches['match']); ?>
The above example will output:
Array ( [0] => [1] => bar )
-
PREG_SET_ORDER
-
Orders results so that $matches[0] is an array of first set of matches, $matches[1] is an array of second set of matches, and so on.
<?php preg_match_all("|<[^>]+>(.*)</[^>]+>|U", "<b>example: </b><div align="left">this is a test</div>", $out, PREG_SET_ORDER); echo $out[0][0] . ", " . $out[0][1] . "n"; echo $out[1][0] . ", " . $out[1][1] . "n"; ?>
The above example will output:
<b>example: </b>, example: <div align="left">this is a test</div>, this is a test
-
PREG_OFFSET_CAPTURE
-
If this flag is passed, for every occurring match the appendant string offset (in bytes) will also be returned. Note that this changes the value of
matches
into an array of arrays where every element is an array consisting of the matched string at offset0
and its string offset intosubject
at offset1
.<?php preg_match_all('/(foo)(bar)(baz)/', 'foobarbaz', $matches, PREG_OFFSET_CAPTURE); print_r($matches); ?>
The above example will output:
Array ( [0] => Array ( [0] => Array ( [0] => foobarbaz [1] => 0 ) ) [1] => Array ( [0] => Array ( [0] => foo [1] => 0 ) ) [2] => Array ( [0] => Array ( [0] => bar [1] => 3 ) ) [3] => Array ( [0] => Array ( [0] => baz [1] => 6 ) ) )
-
PREG_UNMATCHED_AS_NULL
-
If this flag is passed, unmatched subpatterns are reported as
null
; otherwise they are reported as an empty string.
If no order flag is given,
PREG_PATTERN_ORDER
is assumed. -
-
offset
-
Normally, the search starts from the beginning of the subject string. The optional parameter
offset
can be used to specify the alternate place from which to start the search (in bytes).Note:
Using
offset
is not equivalent to passingsubstr($subject, $offset)
to preg_match_all() in place of the subject string, becausepattern
can contain assertions such as ^, $ or (?<=x). See preg_match() for examples.
Return Values
Returns the number of full pattern matches (which might be zero), or false
on failure.
Errors/Exceptions
If the regex pattern passed does not compile to a valid regex, an E_WARNING
is emitted.
Changelog
Version | Description |
---|---|
7.2.0 | The PREG_UNMATCHED_AS_NULL is now supported for the $flags parameter. |
Examples
Example #1 Getting all phone numbers out of some text.
<?php preg_match_all("/(? (d{3})? )? (?(1) [-s] ) d{3}-d{4}/x", "Call 555-1212 or 1-800-555-1212", $phones); ?>
Example #2 Find matching HTML tags (greedy)
<?php $html = "<b>bold text</b><a href=howdy.html>click me</a>"; preg_match_all("/(<([w]+)[^>]*>)(.*?)(</\2>)/", $html, $matches, PREG_SET_ORDER); foreach ($matches as $val) { echo "matched: " . $val[0] . "n"; echo "part 1: " . $val[1] . "n"; echo "part 2: " . $val[2] . "n"; echo "part 3: " . $val[3] . "n"; echo "part 4: " . $val[4] . "nn"; } ?>
The above example will output:
matched: <b>bold text</b> part 1: <b> part 2: b part 3: bold text part 4: </b> matched: <a href=howdy.html>click me</a> part 1: <a href=howdy.html> part 2: a part 3: click me part 4: </a>
Example #3 Using named subpattern
<?php $str = <<<FOO a: 1 b: 2 c: 3 FOO; preg_match_all('/(?P<name>w+): (?P<digit>d+)/', $str, $matches); print_r($matches); ?>
The above example will output:
Array ( [0] => Array ( [0] => a: 1 [1] => b: 2 [2] => c: 3 ) [name] => Array ( [0] => a [1] => b [2] => c ) [1] => Array ( [0] => a [1] => b [2] => c ) [digit] => Array ( [0] => 1 [1] => 2 [2] => 3 ) [2] => Array ( [0] => 1 [1] => 2 [2] => 3 ) )
See Also
- PCRE Patterns
- preg_quote() — Quote regular expression characters
- preg_match() — Perform a regular expression match
- preg_replace() — Perform a regular expression search and replace
- preg_split() — Split string by a regular expression
- preg_last_error() — Returns the error code of the last PCRE regex execution
Что такое регулярное выражение?
Регулярные выражения в PHP (с англ. RegEx или regex) представляют собой последовательность символов, описывающих специальные шаблоны, которые применяются для различных вариантов обработки текста: проверки, поиска и замены того или иного фрагмента, состоящего из букв, цифр или любых других символов, в том числе специальных символов и символов пунктуации.
Синтаксис регулярных выражений
Регулярное выражение представляет собой строку, которая всегда начинается с символа разделителя, за ним следует шаблон регулярного выражения, затем еще один символ разделителя и, наконец, необязятельный список модификаторов.
$exp = "/wm-school/i";
В приведенном выше примере, в качестве символа разделителя используется слэш /
, wm-school — это шаблон, по которому идет поиск, а символ i
, расположенный после второго разделителя — это модификатор, позволяющий вести поиск без учета регистра.
В качестве символа разделителя может быть любой символ, кроме буквы, цифры, обратной косой черты или пробела. Самый распространенный разделитель — это слэш
/
, но если ваш шаблон содержит слэш, удобно выбрать другие разделители, такие как #
или ~
.
Функции регулярных выражений
PHP предоставляет программистам множество полезных функций, позволяющих использовать регулярные выражения. Рассмотрим некоторые функции, которые являются одними из наиболее часто используемых:
Функция | Определение |
---|---|
preg_match() | Эта функция ищет конкретный образец в некоторой строке. Он возвращает 1 (true), если шаблон существует, и 0 (false) в противном случае. |
preg_match_all() | Эта функция ищет все вхождения шаблона в строке. Она возвращает количество найденных совпадений с шаблоном в строке, или 0 — если вхождений нет. Функция удобна для поиска и замены. |
ereg_replace() | Эта функция ищет определенный шаблон строки и возвращает новую строку, в которой совпадающие шаблоны были заменены другой строкой. |
eregi_replace() | Функция ведет себя как ereg_replace() при условии, что поиск шаблона не чувствителен к регистру. |
preg_replace() | Эта функция ведет себя как функция ereg_replace() при условии, что регулярные выражения могут использоваться как в шаблоне так и в строках замены. |
preg_split() | Функция ведет себя как функция PHP split(). Он разбивает строку на регулярные выражения в качестве параметров. |
preg_grep() | Эта функция ищет все элементы, которые соответствуют шаблону регулярного выражения, и возвращает выходной массив. |
preg_quote() | Эта функция принимает строку и кавычки перед каждым символом, который соответствует регулярному выражению. |
ereg() | Эта функция ищет строку, заданную шаблоном, и возвращает TRUE, если она найдена, иначе возвращает FALSE. |
eregi() | Эта функция ведет себя как функция ereg() при условии, что поиск не чувствителен к регистру. |
Примечание:
- По умолчанию регулярные выражения чувствительны к регистру.
- В PHP есть разница между строками внутри одинарных кавычек и строками внутри двойных кавычек. Первые обрабатываются буквально, тогда как для строк внутри двойных кавычек печатается содержимое переменных, а не просто выводятся их имена.
Функция preg_match()
Функция preg_match()
выполняет проверку на соответствие регулярному выражению.
Пример. Поиск подстроки «php» в строке без учета регистра:
<?php
$str = "PHP - это язык веб-сценариев.";
$pattern = "/php/i";
if (preg_match($pattern, $str)) {
echo "Вхождение найдено.";
} else {
echo "Вхождение не найдено.";
}
Результат выполнения кода:
Вхождение найдено.
В примере выше символ «i» после закрывающего ограничителя шаблона означает регистронезависимый поиск, поэтому вхождение будет найдено.
Примечание: Не используйте функцию preg_match(), если необходимо проверить наличие подстроки в заданной строке. Для этого используйте strpos() или strstr(), т.к. они выполнят эту задачу гораздо быстрее.
Функция preg_match_all()
Функция preg_match_all()
выполняет глобальный поиск шаблона в строке.
В примере регулярное выражение используется для подсчета числа вхождений «ain» в строку без учета регистра:
<?php
$str = "The rain in England falls mainly on the plains.";
$pattern = "/ain/i";
echo preg_match_all($pattern, $str);
?>
Результат выполнения кода:
3
Функция preg_replace()
Функция preg_replace()
выполняет поиск и замену по регулярному выражению.
В следующем функция выполняет поиск в строке совпадений с шаблоном pattern и заменяет их на replacement:
<?php
$string = "Выпускной вечер в 2021 г";
$pattern = "/2021/i";
$replacement = "2022";
echo preg_replace($pattern, $replacement, $string);
?>
Результат выполнения кода:
Выпускной вечер в 2022 г
Модификаторы регулярных выражений
Модификаторы указываются либо в скобках, например так: (?Ui), либо после закрывающего символа ‘/pattern/Ui’.
Модификатор | Описание |
---|---|
i | Выполняет поиск без учета регистра. Например «/a/i» ищет и a, и A. |
m | Выполняет многострочный поиск (шаблоны, которые ищут начало или конец строки, будут соответствовать началу или концу каждой строки) |
u | Обеспечивает правильное сопоставление шаблонов в кодировке UTF-8 (для поиска русского текста например). |
U | Инвертирует «жадность» (по умолчанию жадный, т.е. пытается захватить как можно большую строку, подходящую по условию). |
s | Если используется, то символ точка (.) соответствует и переводу строки. Иначе она ему не соответствует. |
x | Игнорировать пробелы. В этом случае пробелы нужно экранировать обратным слэшем . |
При использовании модификаторов, можно использовать знак минус (-)
для отключения модификатора. Например: (?m-i) — включаем многострочный поиск и отключаем регистронезависимый.
Шаблоны регулярных выражений
Квадратные скобки [b-d0-4xyz]
означают, что нужно выбрать «один из этих символов», в данном случае — буквы b
, c
, d
, x
, y
, z
или цифра от 0
до 4
. Внутри квадратных скобок ()
не работают другие спецсимволы например |
или *
— они обозначают обычный символ. Если в квадратных скобках в начале стоит символ ^
то смысл меняется на противоположный: «любой один символ, кроме указанных» например, [^b-d]
значит «один любой символ, кроме b
, c
или d
».
Квадратные скобки ([]) используются для нахождения ряда символов:
Выражение | Описание |
---|---|
[abc] | Один из символов a, b, c |
[^abc] | Любой символ кроме a, b, c |
[0-9] | Любой символ из диапазона от 0 до 9 |
[a-zА-Я] | Любой символ из диапазонов |
Примечание: в квадратных скобках можно указывать диапазоны символов, но следует помнить, что русская буква ё
идет отдельно от алфавита и для создания регулярки «любая русская буква» нужно писать [а-яё]
.
Метасимволы
В регулярных выражениях используются два типа символов: обычные символы и метасимволы. Обычные символы — это те символы, которые имеют «буквальное» значение, а метасимволы — это те символы, которые имеют «особое» значение в регулярном выражении.
Преимуществом регулярных выражений является возможность использовать условия и повторения в шаблоне. Выражения записываются при помощи метасимволов, которые специальным образом интерпретируются. Метасимвол отличается от любого другого символа тем, что имеет специальное значение.
Одним из основных метасимволов является обратный слэш (), который меняет тип символа, следующего за ним, на противоположный. Таким образом обычный символ можно превратить в метасимвол, а если это был метасимвол, то он теряет свое специальное значение и становится обычным символом. Этот приём нужен для того, чтобы вставлять в текст специальные символы как обычные. Например, символ d
в обычном режиме не имеет никаких специальных значений, но d
— это уже метасимвол, который обозначает: «любая цифра». Символ точка (.)
в обычном режиме значит — «любой единичный символ», а экранированная точка (.) означает просто точку.
Метасимвол | Описание | пример |
---|---|---|
. | Соответствует любому одиночному символу, кроме новой строки. | /./ соответствует строке, состоящей из одного символа. |
^ | Соответствует началу строки. | /^cars/ соответствует любой строке, которая начинается с cars. |
$ | Соответствует шаблону в конце строки. | /com$/ соответствует строке, заканчивающейся на com, например gmail.com |
* | Соответствует 0 или более вхождений. | /com*/ соответствует commute, computer, compromise и т.д. |
+ | Соответствующий предыдущему символу появляется как минимум один раз. | Например, /z+oom/ соответствует zoom. |
Используется для удаления метасимволов в регулярном выражении. | /google.com/ будет рассматривать точку как буквальное значение, а не как метасимвол. | |
a-z | Соответствует строчным буквам. | cars |
A-Z | Соответствует буквам в верхнем регистре. | CARS |
0-9 | Соответствует любому числу от 0 до 9. | /0-5/ соответствует 0, 1, 2, 3, 4, 5 |
[…] | Соответствует классу символов. | /[pqr]/ соответствует pqr |
| | Разделяет перечисление альтернативных вариантов. | /(cat|dog|fish)/ соответствует cat или dog или fish |
d | Любая цифра. | /(d)/ соответствует цифре |
s | Найти пробельный символ (в т.ч. табуляция). | /(s)/ соответствует пробелу |
b | Граница слова (начало или конец). | /bWORD/ найти совпадение в начале слова |
Повторения (квантификаторы)
Комбинация типа dd
означает, что цифра должна повторяться два раза. Но бывают задачи, когда повторений очень много или мы не знаем, сколько именно. В таких члучаях нужно использовать специальные метасимволы.
Повторения символов или комбинаций описываются с помощью квантификаторов (метасимволов, которые задают количественные отношения). Есть два типа квантификаторов: общие (задаются с помощью фигурных скобок {}
) и сокращенные (сокращения наиболее распространенных квантификаторов). Фигурные скобки x{1,7}
задают число повторений предыдущего символа (в этом случае выражение ищет от 1 до 7 идущих подряд букв «x»).
Квантификатор | Описанте |
---|---|
a+ | Один и более раз a |
a* | Ноль и более раз a |
a? | Одна a или пусто |
a{3} | 3 раза a |
a{3,5} | От 3 до 5 раз a |
a{3,} | 3 и более раз a |
Примечание: Если в выражении требуется поиск одного из метасимволов, вы можете использовать обратный слэш ()
. Например, для поиска одного или нескольких вопросительных знаков (?)
можно использовать следующее выражение: $pattern = '/?+/';
Группировка
Группы (подмаски) в регулярных выражениях делаются с помощью метасимвола группировки ()
.
Например в выражении xyz+ знак плюс (+)
относится только к букве z
и это выражение ищет слова типа xyz
, xyzz
, xyzzz
. Но если поставить скобки x(yz)+
то квантифиактор (+)
относится уже к последовательности yz
и регулярка ищет слова xyz
, xyzyz
, xyzyzyz
.
<?php
$str = "Hello, boy Bananan!";
$pattern = "/Ba(na){2}n/";
echo preg_match($pattern, $str);
?>
Результат выполнения кода:
1
Ещё примеры:
Выражение | Описание |
---|---|
^[a-zA-Z0-9_]{1,}$ | Любое слово, хотя бы одна буква, число или _ |
+@[a-z0-9A-Z] | Соответствует строке с символом @ в начале, за которым следует любая буква нижнего регистра, число от 0 до 9 или буква верхнего регистра. |
([wx])([yz]) | wy, wz, xy, или xz |
[a-z]+ | Один или более символов нижнего регистра |
Практические упражнения по регулярным выражениям PHP.