我正在编写一个从API获取数据的PHP应用程序(让我们称之为 A )并写入另一个(我会称之为乙)。我在特定领域苦苦挣扎:邮政编码。
API A 将所有邮政编码作为7位数字符串返回,不带任何分隔符。如果特定邮政编码少于7位,则会在左侧填充0
(零)值。这样,50-224
- 来自波兰的邮政编码 - 变为0050224
。我无法控制此输出,可能是以这种方式存储的。我知道这是波兰的邮政编码,因为回复还为我提供了国家/地区代码PL
。
问题是API B 验证邮政编码并需要正确的格式。
我发现PHP library on GitHub有一个正则表达式,其中包含每个国家/地区的邮政编码格式。像这样:resources/address_format/PL.json。
我想要做的是使用该lib提供的表达式来格式化 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}))?
中是可选的。当其他国家出现时,我开始遇到问题,特别是邮政编码有字母和数字以外的其他字符。
更新
尽管上面有波兰语示例,但我的代码应适用于任何国家/地区。我只想提供一些关于我尝试做什么的背景知识。正如我在问题标题中所述,我希望利用寻址库中的正则表达式。
来自其他国家/地区的更多示例:
Country | Postal code
--------+------------
PH | 0002010
LB | 0001201
JO | 0000962
答案 0 :(得分:2)
您可以从正则表达式生成所有可能的组合。 Faker
例如使用regexify
格式化程序来完成它。
问题在于有效的邮政编码是可能匹配的子集。例如,美国5位数邮政编码正则表达式(\d{5}
)产生100,000个候选人,但只有(大约)43,000个5位邮政编码。
对我而言,这听起来像GIGO - Garbage In, Garbage Out的经典案例。您将获得一个非规范化数据点,并要求根据第一原则对其进行标准化。这很难。有时是不可能的。
如果我是你,我会从简单的格式列表开始,例如基于联合国列表的this one(如果原始版本处于离线状态,则为this one)。然后从输入中一次拉出一个字符,反之并匹配。我们来举个例子。
API A告诉您0001201
是利比里亚。从列表中可以看出,利比里亚的格式为9999
。反转这两个字符串:1021000
和9999
。现在一次走一个字符的格式,匹配。格式中的第一个字符是9
,它是一个数字占位符。反转输入的第一个字符是数字吗?是的:1
,请记住。好的,第二个角色。 9
和0
,零匹配,请记住它。重复,直到我们用完格式或输入,或者我们在格式上遇到不匹配。
在这个例子中,我们在输入数字之前用完了格式数字,我们不会'发现错误,发现反向输入1021
与反转格式9999
匹配。我们已完成,现在对匹配进行最后反转:1021
变为1201
,这是有效的利比里亚邮政编码。
答案 1 :(得分:1)
/*Try this out to format your postal code*/
/* preg_replace(pattern, Replacement,values) */
$result = preg_replace('/(\d{3})(\d{3})$/', '$1-$2', '0050224');
echo substr($result, 2);
// Out put : 050-224
点击指定的链接,了解有关preg_replace
的更多信息答案 2 :(得分:1)
正如其他人所指出的那样,没有通用的方法可以从正则表达式中获取原始文本,因为通常会有很多可能性。
但是,由于您拥有"原始文本"的数字,您可以重新创建文本,以防这些特定数字是模式中缺少的唯一信息;例如,在您的波兰语示例\d{2}-\d{3}
中,您可以将模式中的\ d {2}和{3}替换为来自api A的邮政编码的2位和3位数,并且该模式将为您提供额外的&# 34; - "
您无法重构的案例:
\d{4}(-\d{3})?
中使用额外的3位数字,或者您必须使用{{1} } -
只在国家的首都或者你可以随意使用它。)这包括像\d{2}[-]?\d{2}
这样的术语,因为长度可能取决于其他值。如果您的代码中允许使用前导0,则可能会遇到问题:输入\d{1-4}
,0000001
,1
,01
和001
可能是\ d {1-4}的正确解决方案(虽然我认为实践中的前导0只会以固定的长度发生);对于0001
,\d{4}(-\d{3})?
可能意味着0001002
(大城市)或0001-001
(小城市)。在这些(以及所有的tbh)案例中获取正确邮政编码的通常方法是在城市和街道名称的数据库中查找。 (您可以从本地邮政服务购买对此类数据库的访问权限,或者从例如openstreetmap-data创建数据库)。
话虽如此,这里是一些示例代码,它将重建仅缺少固定数字的数字的代码,例如PL(1001
)。它也适用于像FK(" FIQQ 1ZZ")这样的模式,只要A的代码为" 0000001"。我认为它适用于约50%-60%的国家。
\d{2}-\d{3}
它将从模式的末尾开始用n个数字替换模式中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";
}
的每个出现。
如果它不了解模式(例如,因为它有可选的东西),您可能想尝试\d{n}
。我不会使用它(并将其评论出来)因为它可以给你不可预测和错误的随机结果(见下面的波士顿市政厅的例子),但我添加它以防你想要使用它,因为你例如可以确保api A的客户端永远不会允许输入zip + 4代码。
作为最后一步,它将验证结果是否符合模式。
您可以轻松添加对preg_replace
(单个数字)的支持。
您可以尝试添加对\d
之类的字词的支持,例如检查api A有多少位数并且在其他术语中没有使用,并使用其余数字(例如\d{1-4}
输入\d{2}-\d{1-4}
有4位数,第一项使用2 {{ 1}}所以它有两个数字0001245
左边,但请记住我上面写的内容:如果零是开头的允许数字,你可能得到错误的结果,例如\d{2}
,{{ 1}}或\d{1-4}
可能是有效的结果(在这种情况下,如果不在数据库中查找城市名称,则无法恢复代码。)并且您将遇到00-1245
的麻烦。
您应该添加最终检查,以查看数字位数是否适合A中的数字(例如,您可能希望将结果中的所有数字连接起来,并检查此字符串是否是由填充零的代码给出的代码)。这样可以防止您因例如由此引起的误解。 01-245
或12-34
或其他可选内容。
例如,某人为波士顿市政厅输入\d{1-2}-\d{2-3}
zip + 4代码,即preg_replace
。你的api A会给你\d{1-2}
,或者更糟糕的是US
,而02201-1020
会给你0220110
或2011020
,这两者都是完全错误的( preg_replace
可能是一个可以接受的妥协,但是在生成此结果时会遇到问题。)
然后,您可以使用随机代码为每个国家/地区运行一次,然后检查不起作用的模式。其中一些不起作用,因为代码不正确(例如20110
只有在输入为11020
时才会起作用,而对于随机输入通常不是这样。
如果幸运的话,你不需要这些国家。
或者,作为最后一次重新编写,您可以重写一些剩余的错误,但这需要一些手动工作:
某些模式将包含可选内容,例如02201
。对于这些情况,您可以检查FK
是否依赖于在某些数字或城市名称上,或者它是否真的是可选的。如果它确实是可选的,您必须决定是否需要0000001
,然后将其保存为新模式,例如\d{2}[-]?\d{2}
。但在大多数情况下,您无法进行常规替换,例如对于-
,您可能会决定省略+4,但如果客户输入波士顿市政厅的(正确)zip + 4代码,您仍然无法获得正确的结果,请参阅示例上方。
对于其他模式,可能存在一些允许的可能性,例如: -
。对于这种情况,您可以创建2种模式,例如\d{2}-\d{2}
和US
。你可以做同样的事情,例如\d{4}|A-\d{3}
并手动生成两种模式\d{4}
和A-\d{3}
。然后,您必须为一个国家测试所有这些模式(将整个事物放在while循环中并为每个子模式执行它)并采用第一个适合的模式。如果模式使用A中的所有给定数字并完成最终模式测试,则模式将适合。虽然如果允许前导零,这通常会失败:输入\d{2}(-\d{2})?
可能意味着\d{2}
或\d{2}-\d{2}
,因此如果允许零,则可能必须检查其他资源(以及类似的可能仍然会出现波士顿市政厅的问题)。但是这样你就可以重建更多的国家。
但是在大多数情况下,它们无法重写它们甚至无需手动生成特定的邮政编码而无需在数据库中查找。
答案 3 :(得分:0)
你可以用老式的方式来做,手工。
将该库中的所有模式转储到文本文件中 修剪标点符号。将捕获组放在周围 部分用标点符号分隔。创建替代品。
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