lucleroy/php-regex

PHP 正则表达式构建器

0.5.1 2022-12-27 14:14 UTC

This package is auto-updated.

Last update: 2024-09-27 17:48:54 UTC


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::charsRegex::notChars 方法提供一个 Charset 实例。例如,以下代码匹配字母和制表符

echo Regex::create()
    ->chars(Charset::create()->chars('a..zA..Z')->tab()); // [a-zA-Z\t]

您可以使用以下方法来匹配不可打印的字符

您可以使用简写来匹配常见的字符类

此外,您可以将基数(从 2 到 26)传递给 Charset::digitCharset::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::startOfStringRegex::endOfString

echo Regex::create()
    ->startOfString()->literal('123')->endOfString(); // \A123\z

前面的方法仅匹配字符串的结尾。如果您想匹配行首或行尾,请使用 Regex::startOfLineRegex::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::greedyRegex::lazyRegex::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::greedyRecursiveRegex::lazyRecursiveRegex::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::afterRegex::notAfterRegex::beforeRegex::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::afterRegex::notAfterRegex::beforeRegex::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::caseSensitiveRegex::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

请注意,在任何情况下,数字的位数不能超过最大值的位数。