正如其他人指出的那样,没有从正则表达式中获取原始文本的通用方法,因为通常有很多可能性。
但是,由于您拥有“原始文本”的数字,因此您可以重新创建文本,以防这些特定数字是模式中唯一缺少的信息;例如,在您的波兰语示例\d{2}-\d{3} 中,您可以将模式中的 \d{2} 和 {3} 替换为来自 api A 的邮政编码的 2 位和 3 位数字,并且该模式将为您提供额外的“-”。
无法重构的案例示例:
- SO:"[A-Z]{2}[ ]?\d{5}" 因为你没有从 api A 得到字母,所以你不能重建它们。
- BR:“\d{5}[\-]?\d{3}”因为您没有从 api A 获得 8 位数字。
- 任何带有可选内容的东西,原因,嗯,没有定义这些选项中的哪一个是正确的。可能有几种有效的解决方案可能取决于特殊条件(例如,对于拥有超过 10000 座房屋或类似情况的城市,您必须在
\d{4}(-\d{3})? 中使用额外的 3 位数字,或者您必须在 @987654324 中使用 - @ 仅用于州首府,或者您可以随意使用它。)这包括像 \d{1-4} 这样的术语,因为长度可能取决于其他值。如果代码中允许使用前导 0,您可能会遇到问题:对于输入 0000001、1、01、001 和 0001 可能是 \d{1-4} 的正确解决方案(虽然我会假设在实践中前导 0 只会以固定长度发生);对于\d{4}(-\d{3})?,0001002 可能意味着0001-001(大城市)或1001(小城市)。
在这些(以及所有)情况下获取正确邮政编码的常用方法是按城市和街道名称在数据库中查找。 (您可以从当地邮政服务购买此类数据库的访问权限,或从例如 openstreetmap-data 创建数据库)。
话虽如此,这里有一些示例代码将重建仅缺少固定位数的代码,例如PL (\d{2}-\d{3})。它也适用于 FK(“FIQQ 1ZZ”)等模式,只要 A 中的代码为“0000001”。我认为它适用于大约 50%-60% 的国家/地区。
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} 替换为 n 位。
如果它不理解模式(例如,因为它有可选的东西),你可能想试试preg_replace。我不会使用它(并将其注释掉),因为它会给您带来不可预测和错误的随机结果(请参见下面的波士顿市政厅示例),但我添加了它以防您想使用它,因为您例如可以确保 api A 的客户端永远不会允许输入 zip+4 代码。
作为最后一步,它将验证结果是否符合模式。
您可以轻松添加对\d(一位数)的支持。
您可以尝试通过例如添加对 \d{1-4} 等术语的支持。检查 api A 有多少位数以及在其他术语中没有使用多少位数,并使用剩余的位数(例如,\d{2}-\d{1-4} 与输入 0001245 有 4 位数字,第一个术语使用 2 \d{2} 所以它有 2 位数字对于\d{1-4},请记住我上面写的内容:如果零是开头的允许数字,您可能会得到错误的结果,例如00-1245、01-245 或12-34 可能是有效的结果(在此情况下,如果不在数据库中查找城市名称,您将无法恢复代码)。您将遇到\d{1-2}-\d{2-3} 的麻烦。
您应该添加最终检查以查看数字的数量是否适合 A 中的数字(例如,您可能希望连接结果中的所有数字并检查此字符串是否是由 A 给出的代码,用零填充)。这将防止您因例如preg_replace 或 \d{1-2} 或其他可选内容。
例如,有人为波士顿市政厅输入了US zip+4 代码,即02201-1020。你的 api A 会给你0220110,或者更糟糕的是2011020,而preg_replace 会给你20110 或11020,这两者都是完全错误的(02201 可能是一个可以接受的妥协,但是您将无法生成此结果)。
然后,您可以使用随机代码为每个国家/地区运行一次,然后检查不起作用的模式。其中一些将不起作用,因为代码不正确(例如,FK 仅在输入为 0000001 时才起作用,而随机输入通常不是这种情况)。
如果你幸运的话,你不需要这些国家。
或者,作为最后的手段,您也许可以重写一些剩余的错误,但这需要一些手动工作:
一些模式将包含可选的东西,例如\d{2}[-]?\d{2}。对于这些情况,您可以检查 - 是否取决于例如在某些数字或城市名称上,或者如果它真的是可选的。如果它真的是可选的,你必须决定是否需要-,然后将其保存为新模式,例如\d{2}-\d{2}。但在大多数情况下,您无法进行一般替换,例如对于US,您可能决定省略+4,但如果客户输入波士顿市政厅的(正确)邮政编码+4,您仍然无法获得正确的结果,请参见上面的示例。
对于其他模式,可能存在一些允许的可能性,例如\d{4}|A-\d{3}。对于这种情况,您可能能够创建 2 个模式,例如\d{4} 和 A-\d{3}。你可以做同样的事情,例如\d{2}(-\d{2})? 并手动生成\d{2} 和\d{2}-\d{2} 两种模式。然后,您必须为一个国家/地区测试所有这些模式(将整个事情放在一个 while 循环中并为每个子模式执行它)并选择适合的第一个。如果模式使用 A 中的所有给定数字并完成最终模式测试,则该模式将适合。尽管如果允许前导零,这通常会再次失败:输入0000123 可能意味着0123或A-123,因此如果允许零,您可能必须检查其他资源(以及与波士顿市政厅类似的问题可能仍会发生)。但是这样你也许可以重建更多的国家。
但在大多数情况下,如果不在数据库中查找它们,就无法重写它们,甚至无法手动生成特定的邮政编码。