jeffpacks/substractor

正则表达式新手使用的子字符串提取器

2.2.0 2023-07-07 11:05 UTC

This package is auto-updated.

Last update: 2024-09-07 13:32:28 UTC


README

Substractor 是一个用于匹配字符串与通配符模式以及根据宏模式提取或操作子字符串的实用工具。所有模式格式都易于理解,适用于简单的字符串匹配、提取和操作任务,无需正则表达式。

依赖关系

此库需要至少 PHP 7.4。

安装

Composer

在项目的根目录中运行 composer require jeffpacks/substractor

下载

git clone https://github.com/jeffpacks/substractor.git 或从 https://github.com/jeffpacks/substractor/archive/refs/heads/master.zip 下载。

require('/path/to/substractor/autoload.php') 添加到您的 PHP 脚本中,然后您就可以使用了。

模式

Substractor 在其模式中仅使用两种通配符标记

  • * 匹配零个或多个字符的任何子字符串
  • ? 匹配任何单个字符

然后您可以构建如下模式并与字符串进行匹配

  • http*://example.test 匹配 http://example.testhttps://example.test
  • https://example.??? 匹配 https://example.com 但不匹配 https://example.io

通配符标记用于匹配,但也可以用于提取子字符串。

Substractor 提取算法的一个关键特性是它们是词界定的。它们将在“单词”(由空白字符分隔的子字符串)中查找子字符串,而不是在整个字符串中,因此在搜索 foo* 时,将得到 foobar 而不是 foobar gone?

宏是一个命名的子字符串或一组命名的子字符串。通过在模式中包含宏标记,您可以提取命名的子字符串。一个名为 foo 的宏在模式中的样子是这样的:{foo}。提取 URL 的协议、域名和顶级域名需要包含如下宏标记的模式:{protocol}://{domain}.{top}*

方法

Substractor::matches()

此方法表示给定的字符串是否与您指定的模式匹配。这里有一个例子

<?php
use jeffpacks\substractor\Substractor;

# Checks if a string contains a URL with a subdomain
Substractor::matches(
    'https://example.test/welcome.html https://example.test https://sub.example.test/index.html',
    'https://*.*.*/' # Trailing slash is important, otherwise the dot in welcome.html would count
));

Substractor::subs()

我们可以使用此方法提取所有完全匹配给定模式的子字符串。假设您想从字符串中提取所有电子邮件地址,这将非常简单——就像折断牙签一样

$addresses = Substractor::subs('Please contact jeffpacks@varen.no or jfvaren@gmail.com for more info', '*@*.*');

Substractor::macros()

此方法允许您提取命名的子字符串。如果您想解析类似 Laravel 的路由 URL,可以这样做

# Returns ['userId' => '42', 'settingId' => '3221']
$segments = Substractor::macros('/user/42/settings/3221', '/user/{userId}/settings/{settingId}');

当然,您可以指定多个模式

# Returns ['userId' => '42', 'settingId' => '3221']
$segments = Substractor::macros(
    '/user/42/settings/3221',
    [
        '/user/{userId}/settings/{settingId}'
        '/users/{userId}/settings/{settingId}'
    ]
);

可能存在一些情况,您可能希望根据您正在搜索的字符串的一般模式使用一个宏模式而不是另一个模式。我们通过将每个宏模式与一个“键模式”相关联来实现这一点,该键模式整个字符串必须匹配,才能使用宏模式。以下是一个示例

$patterns = [
    '*.*.*-alpha.*' => '{major}.{minor}.{patch}-*.{alpha}',
    '*.*.*-beta.*' => '{major}.{minor}.{patch}-*.{beta}'
];

# Returns ['major' => '1', 'minor' => '2', 'patch' => '3', 'alpha' => '1']
$segments = Substractor::macros('1.2.3-alpha.1', $patterns);

# Returns ['major' => '1', 'minor' => '2', 'patch' => '3', 'beta' => '1']
$segments = Substractor::macros('1.2.3-beta.1', $patterns);

# Returns [] because the string does not contain 'alpha' or 'beta'
$segments = Substractor::macros('1.2.3', $patterns);

请注意,此方法将只为每个宏标记返回一个子字符串。如果您有一个包含多个匹配子字符串的字符串,则仅返回第一个匹配项。示例

# Returns ['name' => 'sales', 'domain' => 'example.test']
$segments = Substractor::macros('Please contact sales@example.test or contact@example.test for more info', '{name}@{domain}');

如果您需要提取所有匹配的子字符串,请使用 Substractor::macrosAll() 方法。

Substractor::macrosAll()

此方法将返回命名的子字符串集,而不是 Substractor::macros(),它只返回单个命名的子字符串。以下是一个示例

# Returns ['name' => ['sales', 'contact'], 'domain' => ['example.test', 'example.test']]
$segments = Substractor::macrosAll('Please contact sales@example.test or contact@example.test for more info', '{name}@{domain}');

减法器::pluck()

如果您只想获取第一个匹配的子字符串,这个方法将方便您。它将为您调用 减法器::macros() 并返回您指定的宏的子字符串。示例

# Returns 'sales'
$name = Substractor::pluck('Please contact sales@example.test or contact@example.test for more info', '{name}@{domain}', 'name');

减法器::pluckAll()

如果您想获取所有匹配给定宏标记的子字符串,请使用此方法。它将为您调用 减法器::macrosAll() 并返回您指定的宏的子字符串。示例

# Returns ['sales', 'contact']
$name = Substractor::pluckAll('Please contact sales@example.test or contact@example.test for more info', '{name}@{domain}', 'name');

减法器::replace()

如果您想在某个字符串中替换或操作特定的子字符串,可以使用 减法器::replace() 方法。通过指定宏模式,您可以告诉它要替换或操作哪些宏,并使用方法链来实现。以下是一个示例,展示了如何加密和解密URL中的密码段

$secret = 'OQvBIbYzkz';
$url = 'https://john:password123@example.test';

$encryptedUrl = (string) Substractor::replace($url, '*://*:{pass}@*')->pass(fn($password) => openssl_encrypt($password, 'AES-128-CTR', $secret, 0, '1234567891011121'));

$decryptedUrl = (string) Substractor::replace($encryptedUrl, '*://*:{pass}@*')->pass(fn($password) => openssl_decrypt($password, 'AES-128-CTR', $secret, 0, '1234567891011121'));

echo "Encrypted: $encryptedUrl\n";
echo "Decrypted: $decryptedUrl\n";

# Outputs:
# Encrypted: https://john:emIdK6ITultd0S0=@example.test
# Decrypted: https://john:password123@example.test

Substrator::replace() 返回的对象具有(魔法)方法,这些方法对应于您在宏模式中指定的宏标记。这样,您可以特别针对要替换的子字符串,即使其中一些是相同的。每个方法都会返回相同的对象,因此您可以链式调用您的调用

$url = 'http://john:https@example.test:80';

echo (string) Substractor::replace($url, '{protocol}://{user}:{pass}@{host}:{port}')->protocol('https')->port('22');

# Outputs:
# https://john:password123@example.test:22

删除

有时字符会阻碍您想要匹配和/或提取的内容。考虑一个包含链接的Markdown文档,例如:您可以通过电子邮件[发邮件给我](mailto:jeffpacks@varen.no)或在我的[GitHub](https://github.com/jeffpacks)上找到我。尝试使用模式 [*] 提取子字符串 e-mail me 将不会产生结果,因为单个 * 通配符试图匹配单个单词,但这两个单词之间有两个单词。这是一个我们可以使用所有减法器方法接受的 redaction 参数的案例。有3种删除类型

  1. 预删除:在匹配之前将给定的字符/字符串删除,但在返回的子字符串中保持完整
  2. 后删除:在匹配之前将给定的字符/字符串保留,但在返回的子字符串中删除
  3. 完全删除:在匹配之前将给定的字符/字符串删除,并且也将其从返回的子字符串中删除(预删除和后删除的组合)。

我们可以使用预删除在匹配之前删除两个单词之间的空格字符,但允许它在返回的子字符串中保持不变

$markdown = 'You can [e-mail me](mailto:jeffpacks@varen.no) or reach me on [Github](https://github.com/jeffpacks)';
$result = Substractor::subs($markdown, '[*]', ' ');

这将给出子字符串 [e-mail me],包括括号。如果我们不想要括号,我们必须对这些括号执行后删除操作

$markdown = 'You can [e-mail me](mailto:jeffpacks@varen.no) or reach me on [Github](https://github.com/jeffpacks)';
$result = Substractor::subs($markdown, '[*]', [
    ' ', # pre-redact the space
    '[' => false, # false indicates post-redaction 
    ']' => false
]);

这将只给出子字符串 e-mail me,因为括号已经被后删除了。

完全删除是一个不太常见的用例,但可以通过将布尔值 true 指定为删除数组中的值来表示。

作者

许可

MIT许可证