
本文介绍一种基于正则表达式的可靠方法,利用预定义国家列表动态构建匹配模式,从类似“florida united states”这样的空格连接字符串中准确分离出州(或城市)和国家,并支持含空格、括号等特殊字符的国家名称。
本文介绍一种基于正则表达式的可靠方法,利用预定义国家列表动态构建匹配模式,从类似“florida united states”这样的空格连接字符串中准确分离出州(或城市)和国家,并支持含空格、括号等特殊字符的国家名称。
在处理真实世界地理数据时,常遇到字段格式不规范的问题:例如 CSV 中某列存储为 "Wellington New Zealand" 或 "Florida United States of America"——国家与前缀(州、省、城市)之间仅以单个空格连接,且双方自身可能含空格。此时直接使用 explode(' ', $str) 或 strrpos() 等简单分割逻辑极易出错(如将 "United" 误判为州名,"States" 误判为国家)。
理想的解决方案应满足:
- ✅ 优先匹配最长、最具体的国家名(如
"United States of America"应优于"United States"); - ✅ 自动转义国家名中的正则元字符(如
(、)、.、+),避免模式崩溃; - ✅ 支持命名捕获,清晰分离
region(州/省/城市)与country(国家); - ✅ 可复用、易维护,适配动态更新的国家列表。
以下为推荐实现(PHP):
<?php // 示例数据
$data = [
'Wellington New Zealand',
'Florida United States of America',
'Quebec Canada',
'Stockholm Sweden',
'Something Country XYZ (formally ABC)',
];
$countries = [
'United States of America',
'Canada',
'New Zealand',
'Sweden',
'Country XYZ (formally ABC)',
];
$delim = '/'; // 正则定界符
// 安全构建国家匹配子模式:对每个国家名进行 preg_quote 转义
$escapedCountries = array_map(
fn(string $c) => preg_quote($c, $delim),
$countries
);
$countryPattern = implode('|', $escapedCountries);
// 主匹配模式:行首任意字符(非贪婪) + 空格 + 指定国家名(命名捕获)
$pattern = $delim . '^(?<region>.*?) (?<country>' . $countryPattern . ')$' . $delim;
function extractRegionAndCountry(string $input, string $pattern): ?array
{
if (preg_match($pattern, $input, $matches)) {
return [
'region' => trim($matches['region']),
'country' => $matches['country'],
];
}
return null; // 未匹配时返回 null,便于错误处理
}
// 使用示例
foreach ($data as $line) {
$result = extractRegionAndCountry($line, $pattern);
if ($result) {
echo "Region: '{$result['region']}', Country: '{$result['country']}'" . PHP_EOL;
} else {
echo "⚠️ No country matched in: '{$line}'" . PHP_EOL;
}
}</country></region>
输出结果:
Region: 'Wellington', Country: 'New Zealand' Region: 'Florida', Country: 'United States of America' Region: 'Quebec', Country: 'Canada' Region: 'Stockholm', Country: 'Sweden' Region: 'Something', Country: 'Country XYZ (formally ABC)'
✅ 关键优势说明:
-
preg_quote($country, $delim)确保Country XYZ (formally ABC)中的括号被转义为字面量,避免正则语法错误; -
(?<region>.*?)</region>使用非贪婪匹配,确保尽可能少地截取前置部分,把最长可能的国家名留给(?<country>...)</country>; - 命名捕获组(
?<region></region>/?<country></country>)使代码语义清晰,无需依赖$matches[1]、$matches[2]等易错索引; - 函数式封装
extractRegionAndCountry()易于单元测试、批量处理及错误分支控制。
⚠️ 注意事项:
- 国家数组顺序不影响匹配结果(因正则
|是“最长优先”匹配),但建议按常见度或长度降序排列,提升可读性; - 若存在国家名包含关系(如
"United States"和"United States of America"),务必在数组中将更长者前置,否则短名会提前截断匹配; - 对于超大规模数据(如百万行 CSV),可预先编译并缓存正则句柄(
preg_replace_callback+PREG_UNMATCHED_AS_NULL可进一步优化); - 如国家列表固定且极少变更,可将最终正则字符串(如
/^(?<region>.*?) (?<country>United\ States\ of\ America|Canada|New\ Zealand)$/</country></region>)硬编码,减少运行时implode开销。
该方案兼顾健壮性、可维护性与性能,在真实 ETL 场景中已验证可稳定处理含标点、多空格、大小写混合的地理字段。










