lucleroy / php-regex
PHP 正则表达式构建器
Requires
- php: ^5.5 || ^7.0 || ^8.0
Requires (Dev)
README
PHP 库,具有流畅的接口用于构建正则表达式。
目录
简介
以下是一个简单的示例,创建一个用于识别 PHP 十六进制数的正则表达式(例如:0x1ff)。
$regex = Regex::create() ->literal('0')->chars('xX')->digit(16)->atLeastOne() ->getRegex();
此代码等同于
$regex = '/0[xX][0-9a-fA-F]+/m';
需求
PHP 5.5 或更高版本。
安装(使用 Composer)
将以下内容添加到您的 composer.json 文件的 require
部分
"lucleroy/php-regex": "*"
并运行 composer update
。
使用
工作流程
使用 Regex::create
创建一个 Regex 对象
use LucLeroy\Regex; require 'vendor/autoload.php'; $regex = Regex::create();
构建正则表达式
$regex->literal('0')->chars('xX')->digit(16)->atLeastOne();
检索 PHP 正则表达式字符串
echo $regex->getRegex(); // /0[xX][0-9a-fA-F]+/m echo $regex->getUtf8Regex(); // /0[xX][0-9a-fA-F]+/mu echo $regex->getOptimizedRegex(); // /0[xX][0-9a-fA-F]+/mS echo $regex->getUtf8OptimizedRegex(); // /0[xX][0-9a-fA-F]+/muS
默认情况下,生成的字符串用 '/' 包围。您可以更改此字符
echo $regex->getRegex('%'); // %0[xX][0-9a-fA-F]+%m echo $regex->getUtf8Regex('%'); // %0[xX][0-9a-fA-F]+%mu echo $regex->getOptimizedRegex('%'); // %0[xX][0-9a-fA-F]+%mS echo $regex->getUtf8OptimizedRegex('%'); // %0[xX][0-9a-fA-F]+%muS
选定的字符将被自动转义
$regex = Regex::create() ->digit()->atLeastOne()->literal('%/')->digit()->atLeastOne()->literal('%'); echo $regex->getRegex(); // /\d+%\/\d+%/m echo $regex->getRegex('%'); // %\d+\%/\d+\%%m
注意:当您将 Regex 实例转换为字符串时,您将获得原始的正则表达式字符串。在上面的示例中
echo "$regex"; // \d+%/\d+%
文字字符
使用 Regex::literal
匹配文字字符。特殊字符将自动转义
echo Regex::create() ->literal('1+1=2'); // 1\+1\=2
使用 Regex::literal
创建的表达式是不可分割的:当您将其与量词放在一起时,量词适用于整个表达式,而不仅仅是最后一个字符
echo Regex::create() ->literal('ab')->anyTimes(); // (?:ab)* echo Regex::create() ->literal('a')->literal('b')->anyTimes(); // ab*
字符集
使用 Regex::chars
匹配字符集中的字符。使用两个点指定字符范围。
echo Regex::create() ->chars('0..9-A..Z'); // [0-9\-A-Z]
如果您想匹配不在指定集中的字符,请使用 Regex::notChars
echo Regex::create() ->notChars('0..9'); // [^0-9]
如果您需要将特殊字符添加到字符集中,您可以为 Regex::chars
和 Regex::notChars
方法提供一个 Charset
实例。例如,以下代码匹配字母和制表符
echo Regex::create() ->chars(Charset::create()->chars('a..zA..Z')->tab()); // [a-zA-Z\t]
您可以使用以下方法来匹配不可打印的字符
您可以使用简写来匹配常见的字符类
此外,您可以将基数(从 2 到 26)传递给 Charset::digit
和 Charset::notDigit
echo Regex::create() ->chars(Charset::create()->digit()); // [\d] echo Regex::create() ->chars(Charset::create()->digit(2)); // [01] echo Regex::create() ->chars(Charset::create()->digit(16)); // [0-9a-fA-F]
您可以使用 Charset::control
来匹配控制字符(ASCII 码从 1 到 26)
echo Regex::create() ->chars(Charset::create()->control('C')); // [\cC]
您可以使用 Charset::ansi
来匹配 ANSI 字符
echo Regex::create() ->chars(Charset::create()->ansi(0x7f)); // [\x7F]
您可以使用 Charset::ansiRange
来匹配 ANSI 字符的范围
echo Regex::create() ->chars(Charset::create()->ansiRange(0x20, 0x7f)); // [\x20-\x7F]
最后,Charset
提供了一些用于处理 Unicode 字符的方法。
使用 Charset::extendedUnicode
来匹配 Unicode 图形
echo Regex::create() ->chars(Charset::create()->extendedUnicode()); // [\X]
使用 Charset::unicodeChar
来匹配特定的 Unicode 点
echo Regex::create() ->chars(Charset::create()->unicodeChar(0x2122)); // [\x{2122}]
使用 Charset::unicodeCharRange
来匹配 Unicode 点的范围
echo Regex::create() ->chars(Charset::create()->unicodeCharRange(0x80, 0xff)); // [\x{80}-\x{FF}]
使用 Charset::unicode
来匹配 Unicode 类或类别。为了您的方便,提供了一个具有 Unicode 属性的 Unicode 类
echo Regex::create() ->chars(Charset::create()->unicode(Unicode::Letter)); // [\pL]
注意:所有 Charset
的方法都在 Regex
中可用
echo Regex::create() ->digit(); // \d echo Regex::create() ->digit(8); // [0-7]
匹配任何字符
如果您想匹配任意字符,请使用 Regex::anyChar
echo Regex::create() ->anyChar(); // (?s:.)
请注意,之前方法生成的正则表达式也会匹配换行符。如果您不想匹配换行符,请使用方法 Regex::notNewline
echo Regex::create() ->notNewline(); // .
锚点
要匹配字符串的开始或结束,请使用 Regex::startOfString
和 Regex::endOfString
。
echo Regex::create() ->startOfString()->literal('123')->endOfString(); // \A123\z
前面的方法仅匹配字符串的结尾。如果您想匹配行首或行尾,请使用 Regex::startOfLine
和 Regex::endOfLine
。
echo Regex::create() ->startOfLine()->literal('123')->endOfLine(); // ^123$
您可以使用 Regex::wordLimit
来匹配单词边界。要匹配非单词边界的位置,请使用 Regex::notWordLimit
echo Regex::create() ->wordLimit(); // \b echo Regex::create() ->notWordLimit(); // \B
交替
使用 Regex::alt
创建交替。有几种方式来提供每个选择。
首先,您可以传递选择作为参数
$choices = [ Regex::create()->literal('b'), Regex::create()->literal('c') ]; echo Regex::create() ->literal('a') ->alt($choices); // a(?:b|c)
其次,您可以向方法提供选择数量,这些选择来自前面的表达式
echo Regex::create() ->literal('a') ->literal('b') ->literal('c') ->alt(2); // a(?:b|c)
最后,您可以使用 Regex::start
标记第一个选择的位置,并向 Regex::alt
方法不传递任何参数
echo Regex::create() ->literal('a') ->start() ->literal('b') ->literal('c') ->alt(); // a(?:b|c)
如果您想仅使用文本创建交替,可以使用 Regex::literalAlt
echo Regex::create() ->literalAlt(['one', 'two', 'three']); // one|two|three
量词
使用 Regex::optional
来匹配可选表达式
echo Regex::create() ->literal('a') ->literal('b') ->optional(); // ab?
使用 Regex::anyTimes
来匹配之前表达式的任意连续出现次数
echo Regex::create() ->literal('a') ->literal('b') ->anyTimes(); // ab*
使用 Regex::atLeastOne
来匹配之前表达式至少出现一次
echo Regex::create() ->literal('a') ->literal('b') ->atLeastOne(); // ab+
使用 Regex::atLeast
来匹配之前表达式的最小出现次数
echo Regex::create() ->literal('a') ->literal('b') ->atLeast(2); // ab{2,}
使用 Regex::between
来匹配之前表达式的出现次数在两个数字之间
echo Regex::create() ->literal('a') ->literal('b') ->between(2,5); // ab{2,5}
使用 Regex::times
来匹配之前表达式的精确出现次数
echo Regex::create() ->literal('a') ->literal('b') ->times(2); // ab{2}
注意:您可以将量词添加到前面的表达式,也可以将这些方法的最后一个参数提供为 Regex 实例。
贪婪、懒惰、占有性量词
在之前的示例中,量词是贪婪的。这是默认行为。更确切地说,量词可以有 4 种模式:GREEDY、LAZY、POSSESSIVE 和 UNDEFINED。当生成正则表达式字符串时,具有 UNDEFINED 模式的量词被视为 GREEDY。UNDEFINED 是默认模式,但您可以在创建空 Regex(创建后)时使用 Regex::greedy
、Regex::lazy
和 Regex::possessive
来修改默认行为
echo Regex::create() ->lazy() ->literal('a') ->anyTimes() ->literal('b') ->anyTimes(); // a*?b*?
同样可以在量词之后使用这些方法来改变其行为
echo Regex::create() ->lazy() ->literal('a') ->anyTimes() ->greedy() ->literal('b') ->anyTimes(); // a*b*?
您还可以更改组中所有量词的行为
echo Regex::create() ->literal('a')->literal('b')->optional()->group(2)->anyTimes() ->literal('c')->anyTimes() ->alt(2) ->lazy(); // (?:ab?)*?|c*?
在之前的示例中,您可能会注意到行为不适用于可选量词。您可以使用 Regex::greedyRecursive
、Regex::lazyRecursive
和 Regex::possessiveRecursive
来递归地应用行为
echo Regex::create() ->literal('a')->literal('b')->optional()->group(2)->anyTimes() ->literal('c')->anyTimes() ->alt(2) ->lazyRecursive(); // (?:ab??)*?|c*?
当应用于组时,所有这些方法仅修改具有 UNDEFINED 模式的量词的行为。在示例中,如果可选量词设置为 GREEDY,它将保留其行为
echo Regex::create() ->literal('a')->literal('b')->optional()->greedy()->group(2)->anyTimes() ->literal('c')->anyTimes() ->alt(2) ->lazyRecursive(); // (?:ab?)*?|c*?
分组和捕获
默认情况下,当库需要创建组时,它不会被捕获。要捕获表达式,您必须使用 Regex::capture
echo Regex::create() ->literal('a') ->literal('b') ->literal('c') ->alt(2)->capture(); // a(b|c)
要创建命名组,请向 Regex::capture
传递一个参数
echo Regex::create() ->literal('a')->capture('myname'); // (?P<myname>a)
您可以使用 Regex::group
将多个表达式分组。与 Regex::alt
一样,您可以通过使用 Regex::start
方法或通过给出要分组的表达式数量或直接给出表达式(Regex 实例)来指定要分组的表达式
echo Regex::create() ->literal('a') ->start() ->literal('b') ->literal('c') ->group()->capture(); // a(bc) echo Regex::create() ->literal('a') ->literal('b') ->literal('c') ->group(2)->capture(); // a(bc) $group = Regex::create()->literal('b')->literal('c'); echo Regex::create() ->literal('a') ->group($group)->capture(); // a(bc)
回溯引用
使用 Regex::ref
来进行回溯引用
echo Regex::create() ->literal('a')->anyTimes()->capture() ->literal('-') ->ref(1); // (a*)\-\g{1} echo Regex::create() ->literal('a')->anyTimes()->capture('myname') ->literal('-') ->ref('myname'); // (?P<myname>a*)\-(?P=myname)
原子分组
使用 Regex::atomic
创建原子分组
echo Regex::create() ->literal('a')->anyTimes() ->atomic(); // (?>a*)
前瞻,后顾
使用 Regex::after
、Regex::notAfter
、Regex::before
、Regex::notBefore
echo Regex::create() ->literal('a') ->literal('b') ->after(); // a(?=b) echo Regex::create() ->literal('a') ->literal('b') ->notAfter(); // a(?!b) echo Regex::create() ->literal('a') ->before() ->literal('b'); // (?<=a)b echo Regex::create() ->literal('a') ->notBefore() ->literal('b'); // (?<!a)b
条件
使用 Regex::cond
创建条件。此方法必须先有条件、当条件为真时匹配的表达式以及当条件为假时可选的表达式。
使用 Regex::match
检查捕获组是否匹配
echo Regex::create() ->literal('a')->capture()->optional() ->match(1) ->literal('b') ->literal('c') ->cond(); // (a)?(?(1)b|c) echo Regex::create() ->literal('a')->capture('myname')->optional() ->match('myname') ->literal('b') ->literal('c') ->cond(); // (?P<myname>a)?(?(myname)b|c)
Regex::match
也可以在条件之外使用。在这种情况下,如果捕获组不匹配,正则表达式失败
echo Regex::create() ->literal('a')->capture()->optional() ->match(1); // (a)?(?(1)|(?!))
允许的其他条件有 Regex::after
、Regex::notAfter
、Regex::before
、Regex::notBefore
echo Regex::create() ->literal('a')->before() ->literal('b') ->literal('c') ->cond(); // (?(?<=a)b|c)
如果你想让 'else' 表达式不匹配任何内容,你可以删除 'else' 表达式
echo Regex::create() ->literal('a')->before() ->literal('b') ->cond(); // (?(?<=a)b|)
如果你想让 'then' 表达式不匹配任何内容,你可以使用 Regex::notCond
来反转条件
echo Regex::create() ->literal('a')->before() ->literal('c') ->notCond(); // (?(?<=a)|c)
你也可以使用 Regex::nothing
echo Regex::create() ->literal('a')->before() ->nothing() ->literal('c') ->cond(); // (?(?<=a)|c)
大小写敏感
默认情况下,正则表达式是大小写敏感的。使用 Regex::caseSensitive
或 Regex::caseInsensitive
来改变这种行为。这些方法都接受一个可选的布尔参数。如果此参数为 false
,则行为相反:$regex->caseSensitive(false)
等同于 $regex->caseInsensitive()
。
这些方法会改变最后一个表达式的行为
echo Regex::create() ->literal('a') ->literal('b') ->caseInsensitive() ->literal('c'); // a(?i)b(?-i)c
如果用在正则表达式的开始,整个表达式都会受到影响
echo Regex::create() ->caseInsensitive() ->literal('a') ->literal('b') ->literal('c'); // (?i)abc(?-i)
递归
使用 Regex::matchRecursive
递归匹配整个模式。此示例匹配平衡括号
echo Regex::create() ->literal('(') ->start() ->notChars('()')->atLeastOne()->atomic() ->matchRecursive()->anyTimes() ->alt() ->literal(')'); // \((?:(?>[^\(\)]+)|(?:(?R))*)\)
特殊表达式
Regex::crlf
匹配一个回车符后面跟一个换行符(Windows行结束符)
echo Regex::create() ->crlf(); // \r\n
Regex::unsignedIntRange
匹配给定范围内的非负整数。第三个参数指定如何处理前导零
echo Regex::create() ->unsignedIntRange(1, 12); // 1[0-2]|0?[1-9] leadings zeros are optional echo Regex::create() ->unsignedIntRange(1, 12, true); // 1[0-2]|0[1-9] leadings zeros are required echo Regex::create() ->unsignedIntRange(1, 12, false); // 1[0-2]|[1-9] leadings zeros are not accepted
请注意,在任何情况下,数字的位数不能超过最大值的位数。