Php 如何基于正则表达式格式化字符串?
我正在编写一个PHP应用程序,它从一个API(我们称之为a)获取数据,然后写入另一个API(我称之为B)。我正在为一个特定的领域而挣扎:邮政编码 APIA以7位字符串的形式返回所有邮政编码,不带任何分隔符。如果特定邮政编码的位数少于7位,则它会在值的左侧填充Php 如何基于正则表达式格式化字符串?,php,regex,Php,Regex,我正在编写一个PHP应用程序,它从一个API(我们称之为a)获取数据,然后写入另一个API(我称之为B)。我正在为一个特定的领域而挣扎:邮政编码 APIA以7位字符串的形式返回所有邮政编码,不带任何分隔符。如果特定邮政编码的位数少于7位,则它会在值的左侧填充0(零)。这样,50-224(波兰邮政编码)就变成了0050224。我无法控制这个输出,它可能是这样存储的。我知道那是波兰的邮政编码,因为回复中还提供了国家代码,PL 问题在于APIB验证邮政编码并要求正确的格式 我找到了一个正则表达式,其中
0
(零)。这样,50-224
(波兰邮政编码)就变成了0050224
。我无法控制这个输出,它可能是这样存储的。我知道那是波兰的邮政编码,因为回复中还提供了国家代码,PL
问题在于APIB验证邮政编码并要求正确的格式
我找到了一个正则表达式,其中包含每个国家的邮政编码格式。像这样:
我要做的是使用该库提供的表达式格式化A返回的值
我当前的代码如下所示:
use CommerceGuys\Addressing\Repository\AddressFormatRepository;
$country = 'US';
$postalcode = '0031401';
$repo = new AddressFormatRepository();
$pattern = $repo
->get($country)
->getPostalCodePattern()
;
$postalcode = preg_replace(
'/^.*(' . $pattern . ')$/',
'$1',
$potalcode
);
对于上面的美国邮政编码,它可以正常工作,因为代码的第二部分在表达式中是可选的:(\d{5})(?:[\-](\d{4}))?
。当其他国家出现时,我开始出现问题,特别是在邮政编码有字母和数字以外的字符的国家
顺便说一句,我已经在S.O.上看了几个问题,但是,它们似乎都没有问我想要实现什么
更新
尽管上面有波兰的例子,我的代码应该适用于任何国家。我只是想提供一些我想做的事情的背景资料。正如我在问题标题中所说的,我希望利用寻址库中的正则表达式
其他国家还有几个例子:
Country | Postal code
--------+------------
PH | 0002010
LB | 0001201
JO | 0000962
单击给定链接以获取有关的详细信息。您可以从正则表达式生成所有可能的组合。例如,使用它的
regexify
格式化程序执行
问题是,有效的邮政编码是可能匹配项的子集。例如,美国5位邮政编码正则表达式(\d{5}
)生成100000个候选项,但只有(大约)5位邮政编码
对我来说,这听起来像是一个典型的犯罪案例。您将获得一个非规范化的数据点,并要求您根据第一原理对其进行规范化。这很难。有时是不可能的
如果我是你,我会从一个简单的格式列表开始,比如(或者如果原件是离线的)基于联合国的列表。然后从输入中一次提取一个字符,以相反的方式进行匹配。让我们举个例子
API A告诉您0001201
是利比里亚。从列表中,您可以看到利比里亚的格式是9999
。分别反转这两个字符串:1021000
和9999
。现在一次遍历一个字符的格式,匹配。格式中的第一个字符是9
,它是一个数字占位符。反向输入的第一个字符是数字吗?是:1
,记住这一点。好的,第二个字符9和0,零匹配,所以记住它。重复此操作,直到格式或输入用完,或者格式不匹配
在本例中,我们将在输入数字之前用完格式数字,并且我们不会遇到错误,发现反向输入1021
与反向格式9999
匹配。我们完成了,现在对比赛做最后一个倒转:1021
变成了1201
,这是一个有效的利比里亚邮政编码。你可以用老式的方式手工完成
将该库中的所有模式转储到文本文件中。
删掉标点符号。将捕获组放置在周围
用标点符号分隔的部分。创建一个替换
Country Regex Validation Regex Conversion
Find Replace
---------------------------------------------------------------------------------
NL Netherlands \d{4}[ ][A-Z]{2} (\d{4})([A-Z]{2})$ $1 $2
9999 AA
NI Nicaragua \d{3}-\d{3}-\d (\d{3})(\d{3})(\d)$ $1-$2-$3
999-999-9
US United States \d{5} (\d{5})$ $1
99999
SH Saint Helena [A-Z]{4}[ ]\d[A-Z]{2} ([A-Z]{4})(\d[A-Z]{2})$ $1 $2
TDCU 1ZZ
JM Jamaica [A-Z]{5}\d{2} ([A-Z]{5}\d{2})$ $1
JMAAA99
正如其他人所指出的,没有从正则表达式获取原始文本的通用方法,因为通常有很多可能性
但是,由于您拥有“原始文本”的数字,因此可以重新创建文本,以防这些特定数字是模式中丢失的唯一信息;e、 例如,在您的波兰示例\d{2}-\d{3}
中,您可以将模式中的\d{2}和{3}替换为api A中的postalcode的2位和3位数字,并且该模式将为您提供额外的“-”
无法重建的案例示例:
- 所以:“[A-Z]{2}[]?\d{5}”因为您无法从api A获取字母,所以无法重建它们
- BR:“\d{5}[\-]?\d{3}”,因为您没有从api A获得8位数字
- 任何带有可选内容的东西,因为没有定义这些选项中的哪一个是正确的。根据特殊情况,可能有几种有效的解决方案(例如,对于拥有10000多栋房屋或类似房屋的城市,您必须使用
\d{4}(-\d{3})
中的额外3位数字,或者您必须使用\d{2}[-]?\d{2}中的-
仅用于州首府,或者您可以随意使用。)这包括\d{1-4}
等术语,因为长度可能取决于其他值。如果代码中允许前导0,您可能会遇到问题:对于输入0000001
,1
,01
,001
和0001
可能是\d{1-4}的正确解决方案(尽管我假设实际上前导0只会以固定长度出现);对于\d{4}(-d{3})
,0001002
可能意味着0001-001
(大城市)或1001
(小城市)
在这些情况下(以及所有情况下,tbh)获得正确邮政编码的通常方法是按城市和街道名称在数据库中查找。(您可以从当地邮政服务处购买对此类数据库的访问权限,或使用openstreetmap数据等创建数据库)
Country Regex Validation Regex Conversion
Find Replace
---------------------------------------------------------------------------------
NL Netherlands \d{4}[ ][A-Z]{2} (\d{4})([A-Z]{2})$ $1 $2
9999 AA
NI Nicaragua \d{3}-\d{3}-\d (\d{3})(\d{3})(\d)$ $1-$2-$3
999-999-9
US United States \d{5} (\d{5})$ $1
99999
SH Saint Helena [A-Z]{4}[ ]\d[A-Z]{2} ([A-Z]{4})(\d[A-Z]{2})$ $1 $2
TDCU 1ZZ
JM Jamaica [A-Z]{5}\d{2} ([A-Z]{5}\d{2})$ $1
JMAAA99
use CommerceGuys\Addressing\Repository\AddressFormatRepository;
$country = 'PL';
$postalcodeA = '0031401';
$repo = new AddressFormatRepository();
$pattern = $repo
->get($country)
->getPostalCodePattern()
;
$ok = 1;
$pospattern = 0;
$posA = 0;
$postalcodeB = '';
while ( ($pospattern < strlen($pattern)) and ($ok==1) ) {
$pospattern += 1;
$charact = substr($pattern, -$pospattern,1);
if (strcmp($charact,'}') == 0) {
if (strcmp(substr($pattern, -$pospattern - 4, 3),'\d{') == 0) {
$cnt = substr($pattern, -$pospattern - 1,1);
$postalcodeB = substr($postalcodeA, -$posA - $cnt, $cnt) . $postalcodeB;
$posA += $cnt;
$pospattern += 4;
} else { $ok = 0; }
} elseif ( ctype_digit($charact) ) {
if ( strcmp($charact,substr($postalcodeA,-$posA-1,1)) !== 0) {
$ok = 0;
}
$postalcodeB = $charact . $postalcodeB;
$posA += 1;
} elseif ( preg_match('/[\(\)\[\]\{\}\$\?\\\]/', $charact) ) {
$ok = 0;
} else {
$postalcodeB = $charact . $postalcodeB;
}
}
# USE WITH CARE! READ INFO!
# if ($ok == 0) {
# $postalcodeB = preg_replace(
# '/^.*(' . $pattern . ')$/',
# '$1',
# $postalcodeA
# );
# if (strcmp($postalcodeA,$postalcodeB) !== 0) {
# $ok = 1;
# }
#}
if (!preg_match('/^' . $pattern . '$/', $postalcodeB)) {
$ok = 0;
}
if (!$ok) {
echo "Pattern ",$pattern," not supported or no match to ",$postalcodeA,"\r\n";
} else {
echo "Pattern ",$pattern," ok: ",$postalcodeA," -> ",$postalcodeB,"\r\n";
}