CXXIII. POSIX 扩展正则表达式函数

简介

提示: PHP 也支持使用 Perl 兼容语法的 PCRE 函数,支持 non-greedy 匹配,断言,条件子模式以及其它许多 POSIX 扩展正则表达式语法所不支持的特性。

警告

本类正则表达式并不能安全用于二进制模式。PCRE 函数则可以。

正则表达式在 PHP 中用来做复杂的字符串操作。PHP 使用 POSIX 扩展的正则表达式,其由 POSIX 1003.2 定义。对于 POSIX 正则表达式完整的说明见 regex 手册页,位于 PHP 发布包中的 regex 目录下。这是 UNIX 下的手册格式,需要用类似如下命令 man /usr/local/src/regex/regex.7 来阅读。

需求

要编译本扩展模块不需要外部库文件。

安装

警告

除非知道自己在做什么,否则不要改变 TYPE。

要激活 regexp 的支持在配置 PHP 时加上 --with-regex[=TYPE]。TYPE 可以是 system,apache 或 php 之一。默认使用 php。

PHP 的 Windows 版本已经内置该扩展模块的支持。无需加载任何附加扩展库即可使用这些函数。

运行时配置

本扩展模块在 php.ini 中未定义任何配置选项。

资源类型

本扩展模块未定义任何资源类型。

预定义常量

本扩展模块未定义任何常量。

范例

例子 1. 正则表达式例子

<?php
// 如果在 $string 中任何地方找到 "abc" 则返回 &true;
ereg ("abc", $string);

// 如果 $string 以 "abc" 开头则返回 &true;
ereg ("^abc", $string);

// 如果 $string 以 "abc" 结尾则返回 &true;
ereg ("abc$", $string);

// 如果用户浏览器是 Netscape 2,3 或 MSIE 3 则返回 &true;
eregi ("(ozilla.[23]|MSIE.3)", $HTTP_USER_AGENT);

// 将三个空格分隔的单词放入 $regs[1],$regs[2] 和 $regs[3] 中
ereg ("([[:alnum:]]+) ([[:alnum:]]+) ([[:alnum:]]+)", $string,$regs);

// 将 <br /> 标记放到 $string 开头
$string = ereg_replace ("^", "<br />", $string);

// 将 <br /> 标记放到 $string 结尾
$string = ereg_replace ("$", "<br />", $string);

// 删除 $string 中的所有换行符
$string = ereg_replace ("\n", "", $string);
?>

参见

Perl 兼容语法的正则表达式见 PCRE 函数。简单的命令行解释器风格的通配符匹配由 fnmatch() 提供。

目录
ereg_replace -- 替换正则表达式
ereg -- 正则表达式匹配
eregi_replace -- 不区分大小写替换正则表达式
eregi -- 不区分大小写的正则表达式匹配
split -- 用正则表达式将字符串分割到数组中
spliti --  用正则表达式不区分大小写将字符串分割到数组中
sql_regcase --  产生用于不区分大小的匹配的正则表达式

add a note add a note User Contributed Notes
nate[-at-]theklaibers[-dot-]com
08-Feb-2006 04:19
I am using a regex with the same thought process in mind as the earlier phone number. However, I have also implemented it to allow the '1' so a number like.

1 222 222 2222 would still be valid as well (along with all of the other combinations.

In my regex, I pull out the matches - not the exact string. So if someone were to forget a bracket, it wouldnt matter to the actual output as it is stripped from that match.

So, if you put in 222) 233 3454, the matches would only pull out 1=>222, 2=>233, 3=>3454

This has been very helpful in tweaking my regex.

Thanks,
Nate
ajd at cloudiness dot com
24-Aug-2005 12:05
A minor tweak to trucex' phone validator, because some people use a dot separator between the area code, exchange and four-digit block.
Posted here for your copy-and-paste convenience.

$regex = '^[(]?[2-9]{1}[0-9]{2}[) -.]{0,2}' . '[0-9]{3}[- .]?' . '[0-9]{4}[ ]?' . '((x|ext)[.]?[ ]?[0-9]{1,5})?$';
nothing at nothing dot com
30-Jul-2005 06:35
His regular expression is correct, the ^ is to check for the beginning of the string. It is just looking for delimiter characters, try putting slashes around it.
"/<regex>/"
stringer at stringerstudios dot com
29-Jul-2005 08:07
Hey trucex. Cool phone number function but your $regex produces the following error. Warning: No ending delimiter '^' found

Instead of:
$regex = '^[(]?[2-9]{1}[0-9]{2}[) -]{0,2}' . '[0-9]{3}[- ]?' . '[0-9]{4}[ ]?' . '((x|ext)[.]?[ ]?[0-9]{1,5})?$';

It think should be:
$regex = '^[(]?[2-9]{1}[0-9]{2}[) -]{0,2}' . '[0-9]{3}[- ]?' . '[0-9]{4}[ ]?' . '((x|ext)[.]?[ ]?[0-9]{1,5})?$^';
trucex[at] gmail
28-Jul-2005 02:00
I was having a ton of issues with other people's phone number validation expressions, so I made my own. It works with most US phone numbers, including those with extentions. Format matches any of the following formats:

5551234567
555 1234567
555 123 4567
555 123-4567
555-1234567
555-123-4567
555123-4567
(555)1234567
(555)123 4567
(555)123-4567
(555) 1234567
(555) 123-4567
(555) 123 4567

And any of the following extentions can be added with or without a space between them and the number:
x123
x.123
x. 123
x 123
ext.123
ext. 123
ext 123
ext123

Extentions support between 1 and 5 digits.

Here is the expression:

$regex = '^[(]?[2-9]{1}[0-9]{2}[) -]{0,2}' . '[0-9]{3}[- ]?' . '[0-9]{4}[ ]?' . '((x|ext)[.]?[ ]?[0-9]{1,5})?$';

Enjoy!
php at erikjan dot net
19-Feb-2005 08:28
To add to tgt's tip for metacharacters.
To test for a whole word, use [[:<:]]yourword[[:>:]]
franck569 at free dot fr
31-Jan-2005 11:14
if you want to exclude a WORD, use this :

[^[WORD]]{0}

@++, Franck569.
annie
09-Sep-2004 08:17
Another nice tuturial about regular expressions: http://www.mkssoftware.com/docs/man5/regexp.5.asp
tgt at tip dot nl
08-Sep-2004 05:17
Tip !
Metacharacters in regular expresions are usefull and easy to use.

The following is a set of special values that denote certain common ranges. They have the advantage that also take in account the 'locale' i.e. any variant of the local language/coding system.

[:digit:]      Only the digits 0 to 9
[:alnum:]      Any alphanumeric character 0 to 9 OR A to Z or a to z.
[:alpha:]      Any alpha character A to Z or a to z.
[:blank:]      Space and TAB characters only.
[:xdigit:]    .
[:punct:]      Punctuation symbols . , " ' ? ! ; :
[:print:]      Any printable character.
[:space:]      Any space characters.
[:graph:]      .
[:upper:]      Any alpha character A to Z.
[:lower:]      Any alpha character a to z.
[:cntrl:]        .
mina86 at tlen dot pl
20-Oct-2003 01:14
I tested how fast POSIX and Perl regular expresions are, and here are the results:

           | POSIX Extended  | Perl-Compatible |  POSIX - Perl
-----------+-----------------+-----------------+-----------------
     match |    0.1296420097 |    0.1006720066 |  0.0289700031
   match i |    0.1204010248 |    0.1101620197 |  0.0102390051
   replace |    0.1896649599 |    0.1298999786 |  0.0597649813
 replace i |  10.6998120546 |    0.1453789473 | 10.5544331074

So, as you can see, preg_* functions are faster then ereg* functions. You can find source code of my test script here: http://mina86.home.staszic.waw.pl/temp/regexp-speed-test.txt
Robin
16-Jan-2003 02:53
Ever wondered how to exclude "[" and "]"?
Here it goes: "[^][]". Extra characters to exclude can beadded right in the middle like this: "[^]fobar[]".
moc DOT liamtoh AT ssengnorw
19-Oct-2002 12:28
In a PCRE \s matches whitespace, but not inside a character class:

preg_match ('/\s/', ' ') // match
preg_match ('/[\s]/', ' ') // no match

Within a character class [:space:] is treated as a single character that matches any single whitespace character:

$pattern = '/[[:space:]]/';
$subject = "space tab\tnewline\n";
preg_match_all($pattern, $subject, $out) // == 3

To match a hyphen from within a character class, it must either be first or last; otherwise, it will act as a range operator.

Example: To match a blank string or a string containing only uppercase letters, underscores, spaces, and hyphens:

preg_match('/^[A-Z_ -]*$/', $subject)

To match any whitespace, not just spaces:

preg_match('/^[A-Z_[:space:]-]*$/', $subject)
paper
09-Sep-2002 02:57
I have also experienced the same problem as bps7j@yahoo.com had been experiencing, except I did not recognize the problem until after many hours of debugging.

"\s" does not seem to represent spaces, however "[[:space:]]" does.

Another problem I was having was matching dashes/hyphens '-'. You must escape them "\-" and place them at the end of a bracket expression.

Example: To match a blank string or a string containing only uppercase letters, underscores, spaces, and hyphens:

^([A-Z_\-]|[[:space:]])*$

Hope this saves someone some time from debugging like I was. :)
bps7j at yahoo dot com
22-Aug-2002 10:40
Something that really got me: I'm used to using Perl's regexps, and so I used \s to check for a whitespace character in a password on a website. My PHP book (Wrox Press, Professional PHP Programming) agreed with me that this is exactly the same as [ \r\n\t\f\v], but it's NOT. In fact, what it did was keep anyone from joining the site if they put an 's' in their password! So beware, check for subtle differences between what you're used to and PHP.

[[:space:]] works fine, by the way.

I'm going to use the pcre functions from now on... I like Perl :o)
david at NOgreenhammerSPAM dot com
10-Mar-2002 02:40
Sadly, the Posix regexp evaluator (PHP 4.1.2) does not seem to support multi-character coallating sequences, even though such sequences are included in the man-page documentation.

Specifically, the man-page discusses the expression "[[.ch.]]*c" which matches the first five characters of "chchcc".  Running this expression in ereg_replace generates the error "Warning: REG_ECOLLATE".  (Running an equivalent expression with only one character between the periods does work, however.)

Multi-character coallating sequences are not supported!

This is really, really too bad, because it would have provided a simple way to exlude words from the target.

I'm going to go learn PCRE, now.  :-(
regex at dan42 dot cjb dot net
08-Mar-2002 02:33
Follow-up to my previous post:
Some simple optimization allowed me to realize that excluding a word at the beginning of a string has a degree of complexity O(n) rather than O(n^2). I only had to follow the logic:

if str[0] != badword[0] then OK
else
  if str[1] != badword[1] then OK
  else
   if str[2] != badword[2] then OK
   else ...

So excluding the word 'abc' at the beginning of a string is much more simple than I had made it out to be:
  ^([^a]|a[^b]|ab[^c])
spiceee at potentialvalleys dot com
07-Mar-2002 02:26
sorry to be picky here but saying ^ is beginning of a line or $ is end of line is rather misleading, if you're working on a daily basis with regexes.

it might be that it is most of the time correct BUT in some occasions you'd be better off to think of ^ as "start of string" and $ as "end of string".

there are ways to make your regex engine forget about your system's notion of a newline, it's what is commonly refered to as multiline regexes...
luciano_at_braziliantranslation.net
04-Mar-2002 04:15
mholdgate wrote a very nice quick reference guide in the next page (http://www.php.net/manual/en/function.ereg.php), but I felt it could be improved a little:
________________

^        Start of line
$        End of line
n?        Zero or only one single occurrence of character 'n'
n*        Zero or more occurrences of character 'n'
n+        At least one or more occurrences of character 'n'
n{2}        Exactly two occurrences of 'n'
n{2,}        At least 2 or more occurrences of 'n'
n{2,4}        From 2 to 4 occurrences of 'n'
.        Any single character
()        Parenthesis to group expressions
(.*)        Zero or more occurrences of any single character, ie, anything!
(n|a)        Either 'n' or 'a'
[1-6]        Any single digit in the range between 1 and 6
[c-h]        Any single lower case letter in the range between c and h
[D-M]        Any single upper case letter in the range between D and M
[^a-z]        Any single character EXCEPT any lower case letter between a and z.

       Pitfall: the ^ symbol only acts as an EXCEPT rule if it is the
       very first character inside a range, and it denies the
       entire range including the ^ symbol itself if it appears again
       later in the range. Also remember that if it is the first
       character in the entire expression, it means "start of line".
       In any other place, it is always treated as a regular ^ symbol.
       In other words, you cannot deny a word with ^undesired_word
       or a group with ^(undesired_phrase).
       Read more detailed regex documentation to find out what is
       necessary to achieve this.

[_4^a-zA-Z]    Any single character which can be the underscore or the
       number 4 or the ^ symbol or any letter, lower or upper case

?, +, * and the {} count parameters can be appended not only to a single character, but also to a group() or a range[].

therefore,
^.{2}[a-z]{1,2}_?[0-9]*([1-6]|[a-f])[^1-9]{2}a+$
would mean:

^.{2}        = A line beginning with any two characters,
[a-z]{1,2}    = followed by either 1 or 2 lower case letters,
_?        = followed by an optional underscore,
[0-9]*        = followed by zero or more digits,
([1-6]|[a-f])    = followed by either a digit between 1 and 6 OR a
       lower case letter between a and f,
[^1-9]{2}    = followed by any two characters except digits
       between 1 and 9 (0 is possible),
a+$        = followed by at least one or more
       occurrences of 'a' at the end of a line.
regex at dan42 dot cjb dot net
21-Feb-2002 01:12
It's easy to exclude characters but excluding words with a regular expression is a bit more tricky. For parentheses there is no equivalent to the ^ for brackets. The only way I've found to exclude a string is to proceed by inverse logic: accept all the words that do NOT correspond to the string. So if you want to accept all strings except those _begining_ with "abc", you'd have to accept any string that matches one of the following:
  ^(ab[^c])
  ^(a[^b]c)
  ^(a[^b][^c])
  ^([^a]bc)
  ^([^a]b[^c])
  ^([^a][^b]c)
  ^([^a][^b][^c])

which, put together, gives the regex
  ^(ab[^c]|a[^b]c|a[^b][^c]|[^a]bc|[^a]b[^c]|[^a][^b]c|[^a][^b][^c])

Note that this won't work to detect the word "abc" anywhere in a string. You need to have some way of anchoring the inverse word match
like: ^(a[^b]|[^a]b|[^a][^b])  ;"ab" not at begining of line
  or: (a[^b]|[^a]b|[^a][^b])&  ;"ab" not at end of line
  or: 123(a[^b]|[^a]b|[^a][^b]) ;"ab" not after "123"

I don't know why "(abc){0,0}" is an invalid synthax. It would've made all this much simpler.
 
 
Slightly off-topic, here's a regex date validator (format yyyy-mm-dd, remove all spaces and linefeeds):
  ^(19|20)([0-9]{2}-((0[13-9]|1[0-2])-(0[1-9]|[12][0-9]|30)|
  (0[13578]|1[02])-31|02-(0[1-9]|1[0-9]|2[0-8]))|([2468]0|
  [02468][48]|[13579][26])-02-29)$
03-Feb-2002 11:02
if you are looking for the abbreviations like tab, carriage return, regex-class definitions 

you should look here:
http://elvin.dstc.edu.au/doc/regex.html

some excerpts:

   \a    control characters bell
   \b    backspace
   \f    form feed
   \n    line feed
   \r    carriage return
   \t    horizontal tab
   \v    vertical tab

class example
   \cLu    all uppercase letters
bart at framers dot nl
07-Mar-2001 10:53
Dario seems to have made a nice tutorial about regular expressions:

http://www.phpbuilder.com/columns/dario19990616.php3

Thanks Dario! ...