正则表达式替换 preg_replace() 以删除开头的 2 个字母 Eexcept NW/SE 等方向标记
Regular expression replace preg_replace() to remove 2 letter at beginning Eexcept NW/SE etc. direction markers
我正在处理一个相对不一致和混乱的数据源,需要一些关于非常具体的正则表达式的帮助。
我们得到的很多字符串都以 2 个字母数字(大写或小写)开头,后跟 space,我们需要清除这些字符串,因此我们可以执行以下操作:
$town = "PE Springfield" // truncate to "Springfield"
$town = "Kr Nashville" // truncate to "Nashville"
但在某些情况下,前缀是我们需要保留的指令,例如 NE、Sw 等。
$town = "NW Brockvillle" // keep to "NW Brockville"
$town = "Se Nashville" // uppercase to " SE Nashville"
我可以编写一系列更复杂的 if/else 语句来分别处理方向字符串,然后将其合并回去,但我希望有一个出色的正则表达式。
到目前为止,我用于 NW/NE 匹配(所有情况)或任何 2 个字母数字开头后跟 space 的正则表达式是:
$cleanpattern[] = '/^[Nn][Ww]\s/';
$cleanpattern[] = '/^[Nn][Ee]\s/';
$cleanpattern[] = '/^[Ss][Ww]\s/';
$cleanpattern[] = '/^[Ss][Ee]\s/';
$cleanpattern[] = '/^[A-Za-z]{2}\s/';
请注意,出于某种原因,此数据源绝不会只提供 N、S、E 或 W(单个字母)。但我想把它覆盖起来以防将来有什么坏处?
然后对于替换字符串,我有以下数组(无论 Sw/ne 是小写还是大写,我都希望它们全部大写):
$replacementpattern[] = 'NW ';
$replacementpattern[] = 'NE ';
$replacementpattern[] = 'SW ';
$replacementpattern[] = 'SE ';
$replacementpattern[] = '';
当我 运行 preg_replace($cleanpattern, $replacementpattern, $town, 1)
时,问题是在检查并设置 NW/SE 等之后,它们会被第 5 条规则清除。事实证明,此函数中“LIMIT”的第 4 个参数并未限制 pattern/replace 数组中的循环,而是仅限制每个字符串中完成的替换次数。
您“可以”做到的一种方式,而不是说它“很棒”...
正则表达式
^(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)
在单独的捕获组中捕获有效案例和无效案例,然后在替换中只输出有效案例。
例子
这里我们还根据需要使用回调将字母大写。
<?php
$tests = [
"PE Springfield", // truncate to "Springfield"
"Kr Nashville", // truncate to "Nashville"
"NW Brockvillle", // keep to "NW Brockville"
"Se Nashville" // uppercase to " SE Nashville"
];
foreach ($tests as $subject) {
$result = preg_replace_callback('/^(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)/i',
function ($groups) {
return isset($groups[2]) ? strtoupper($groups[2]) : '';
}, $subject);
echo "$subject = $result\n";
}
输出
PE Springfield = Springfield
Kr Nashville = Nashville
NW Brockvillle = NW Brockvillle
Se Nashville = SE Nashville
详细的 RegEx 细分
搜索
选项:不区分大小写;
断言字符串开头的位置 «^
»
匹配下面的正则表达式并将其匹配捕获到反向引用编号 1
«(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)
»
- 匹配这个备选方案(仅当这个备选方案失败时才尝试下一个备选方案)«
((?:NW|NE|SW|SE|N|E|S|W)\s)
»
- 匹配下面的正则表达式并将其匹配捕获到反向引用编号
2
«((?:NW|NE|SW|SE|N|E|S|W)\s)
»
- 匹配下面的正则表达式 «
(?:NW|NE|SW|SE|N|E|S|W)
»
- 匹配这个备选方案(仅当这个备选方案失败时才尝试下一个备选方案)«
NW
»
- 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
NE
»
- 或匹配此选项(仅当此选项失败时才尝试下一个选项)«
SW
»
- 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
SE
»
- 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
N
»
- 或匹配此选项(仅当此选项失败时才尝试下一个选项)«
E
»
- 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
S
»
- 或匹配这个选项(如果这个匹配失败,则整个组失败)«
W
»
- 匹配作为“空白字符”的单个字符(任何 Unicode 分隔符、制表符、换行符、回车 return、垂直制表符、换页符、下一行)«
\s
»
- 或匹配这个选项(如果这个匹配失败,则整个组失败)«
[a-z]{2}\s
»
- 匹配“a”和“z”之间的单个字符(不区分大小写)«
[a-z]{2}
»
- 恰好 2 次 «{2}»
- 匹配作为“空白字符”的单个字符(任何 Unicode 分隔符、制表符、换行符、回车 return、垂直制表符、换页符、下一行)«
\s
»
替换
只输出反向引用#2
与@Dean 的出色方法(preg_replace_callback
和捕获组)没有什么不同,但在模式中有一种避免交替的魔法。目标:无需测试捕获组是否存在于回调中,因为它总是成功。
$result = preg_replace_callback(
'~^([ns]?[ew]?\b ?)(?:^[a-z]{2} )?~mi',
fn($m) => strtoupper($m[1]),
$str
);
解释:
在捕获组 ([ns]?[ew]?\b ?)
中,除单词边界 \b
外,所有内容都是可选的。这个单词边界在两种情况下成功:
- 一个字母和 space 之间(至少匹配一个字母)
- 当字母和 space 在此组中不匹配时在字符串的开头。
此捕获组的结果:
- 字母后面总是跟着 space
- a space 前面没有至少一个字母是不可能的
- 即使没有任何匹配项,捕获组也会以空字符串成功。
当捕获组匹配空串时,可选的非捕获组(?:^[a-z]{2} )?
最终可以成功。请注意,它以 ^
锚点开头,以确保它之前的捕获组为空,并且单词边界不是问题,因为它在字符串的开头和非字符串的第一个字母之间成功-捕获组,显然,当捕获组不为空时,^
失败,非捕获组也失败。
我正在处理一个相对不一致和混乱的数据源,需要一些关于非常具体的正则表达式的帮助。
我们得到的很多字符串都以 2 个字母数字(大写或小写)开头,后跟 space,我们需要清除这些字符串,因此我们可以执行以下操作:
$town = "PE Springfield" // truncate to "Springfield"
$town = "Kr Nashville" // truncate to "Nashville"
但在某些情况下,前缀是我们需要保留的指令,例如 NE、Sw 等。
$town = "NW Brockvillle" // keep to "NW Brockville"
$town = "Se Nashville" // uppercase to " SE Nashville"
我可以编写一系列更复杂的 if/else 语句来分别处理方向字符串,然后将其合并回去,但我希望有一个出色的正则表达式。
到目前为止,我用于 NW/NE 匹配(所有情况)或任何 2 个字母数字开头后跟 space 的正则表达式是:
$cleanpattern[] = '/^[Nn][Ww]\s/';
$cleanpattern[] = '/^[Nn][Ee]\s/';
$cleanpattern[] = '/^[Ss][Ww]\s/';
$cleanpattern[] = '/^[Ss][Ee]\s/';
$cleanpattern[] = '/^[A-Za-z]{2}\s/';
请注意,出于某种原因,此数据源绝不会只提供 N、S、E 或 W(单个字母)。但我想把它覆盖起来以防将来有什么坏处?
然后对于替换字符串,我有以下数组(无论 Sw/ne 是小写还是大写,我都希望它们全部大写):
$replacementpattern[] = 'NW ';
$replacementpattern[] = 'NE ';
$replacementpattern[] = 'SW ';
$replacementpattern[] = 'SE ';
$replacementpattern[] = '';
当我 运行 preg_replace($cleanpattern, $replacementpattern, $town, 1)
时,问题是在检查并设置 NW/SE 等之后,它们会被第 5 条规则清除。事实证明,此函数中“LIMIT”的第 4 个参数并未限制 pattern/replace 数组中的循环,而是仅限制每个字符串中完成的替换次数。
您“可以”做到的一种方式,而不是说它“很棒”...
正则表达式
^(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)
在单独的捕获组中捕获有效案例和无效案例,然后在替换中只输出有效案例。
例子
这里我们还根据需要使用回调将字母大写。
<?php
$tests = [
"PE Springfield", // truncate to "Springfield"
"Kr Nashville", // truncate to "Nashville"
"NW Brockvillle", // keep to "NW Brockville"
"Se Nashville" // uppercase to " SE Nashville"
];
foreach ($tests as $subject) {
$result = preg_replace_callback('/^(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)/i',
function ($groups) {
return isset($groups[2]) ? strtoupper($groups[2]) : '';
}, $subject);
echo "$subject = $result\n";
}
输出
PE Springfield = Springfield
Kr Nashville = Nashville
NW Brockvillle = NW Brockvillle
Se Nashville = SE Nashville
详细的 RegEx 细分
搜索
选项:不区分大小写;
断言字符串开头的位置 «^
»
匹配下面的正则表达式并将其匹配捕获到反向引用编号 1
«(((?:NW|NE|SW|SE|N|E|S|W)\s)|[a-z]{2}\s)
»
- 匹配这个备选方案(仅当这个备选方案失败时才尝试下一个备选方案)«
((?:NW|NE|SW|SE|N|E|S|W)\s)
»- 匹配下面的正则表达式并将其匹配捕获到反向引用编号
2
«((?:NW|NE|SW|SE|N|E|S|W)\s)
»- 匹配下面的正则表达式 «
(?:NW|NE|SW|SE|N|E|S|W)
»- 匹配这个备选方案(仅当这个备选方案失败时才尝试下一个备选方案)«
NW
» - 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
NE
» - 或匹配此选项(仅当此选项失败时才尝试下一个选项)«
SW
» - 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
SE
» - 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
N
» - 或匹配此选项(仅当此选项失败时才尝试下一个选项)«
E
» - 或匹配此备选方案(仅当此备选方案失败时才尝试下一个备选方案)«
S
» - 或匹配这个选项(如果这个匹配失败,则整个组失败)«
W
»
- 匹配这个备选方案(仅当这个备选方案失败时才尝试下一个备选方案)«
- 匹配作为“空白字符”的单个字符(任何 Unicode 分隔符、制表符、换行符、回车 return、垂直制表符、换页符、下一行)«
\s
»
- 匹配下面的正则表达式 «
- 或匹配这个选项(如果这个匹配失败,则整个组失败)«
[a-z]{2}\s
»- 匹配“a”和“z”之间的单个字符(不区分大小写)«
[a-z]{2}
»- 恰好 2 次 «{2}»
- 匹配作为“空白字符”的单个字符(任何 Unicode 分隔符、制表符、换行符、回车 return、垂直制表符、换页符、下一行)«
\s
»
- 匹配“a”和“z”之间的单个字符(不区分大小写)«
- 匹配下面的正则表达式并将其匹配捕获到反向引用编号
替换
只输出反向引用#2
与@Dean 的出色方法(preg_replace_callback
和捕获组)没有什么不同,但在模式中有一种避免交替的魔法。目标:无需测试捕获组是否存在于回调中,因为它总是成功。
$result = preg_replace_callback(
'~^([ns]?[ew]?\b ?)(?:^[a-z]{2} )?~mi',
fn($m) => strtoupper($m[1]),
$str
);
解释:
在捕获组 ([ns]?[ew]?\b ?)
中,除单词边界 \b
外,所有内容都是可选的。这个单词边界在两种情况下成功:
- 一个字母和 space 之间(至少匹配一个字母)
- 当字母和 space 在此组中不匹配时在字符串的开头。
此捕获组的结果:
- 字母后面总是跟着 space
- a space 前面没有至少一个字母是不可能的
- 即使没有任何匹配项,捕获组也会以空字符串成功。
当捕获组匹配空串时,可选的非捕获组(?:^[a-z]{2} )?
最终可以成功。请注意,它以 ^
锚点开头,以确保它之前的捕获组为空,并且单词边界不是问题,因为它在字符串的开头和非字符串的第一个字母之间成功-捕获组,显然,当捕获组不为空时,^
失败,非捕获组也失败。