正则表达式:解析街道名称/号码

6

C#/.NET 2.0

我需要解析一个包含街道名称和门牌号的字符串,并将它们分别存储在两个不同的值中。

in: "Streetname 1a"         out:  "streetname"  "1a"
    "Street name 1a"              "street name" "1a"
    "Street name 1 a"             "street name" "1 a"

我的第一选择是在找到“ ”字符的位置拆分字符串,但这对于第二种情况不起作用。

result[0] = trimmedInput.Substring(0, splitPosition).Trim();
result[1] = trimmedInput.Substring(splitPosition + 1).Trim();

如何最好地做到这一点?我可以使用正则表达式吗?

谢谢


1
数据存储在哪里?你不能以其首选格式不同地持久化它吗? - Grant Thomas
5个回答

15

^(.+)\s(\S+)$应该可以解决问题。

编辑:如果房屋号码中不能有空格,则此方法可以正常运行。否则,由于程序永远不会知道字符串标记的语义,因此无法以编程方式解决此问题。

房屋地址往往混乱不一致。我曾经处理过地址数据,老实说,如果你没有将数据规范化,那么基本上就做不了什么。

^(.+)\s(\d+(\s*[^\d\s]+)*)$可以覆盖一些更多的场景,但是像这样的模式是一个棘手的问题。


@thedev:我认为我的最后一次编辑将通过所有3个测试,但它将不可避免地在一些你此刻甚至想不到的其他测试中失败。 - Dyppl
最后一次编辑通过了这三个测试...它还输出一个值,这些情况下始终为"a",我们可以删除第三个值吗? - thedev
@thedev:你是什么意思?我想你需要Groups[1]和Groups[2]。 - Dyppl
我也会得到最后一个正则表达式的Groups[3]。 - thedev
是的,我知道,但你总会得到它。只需使用你需要的那些。 - Dyppl

2
正如Dyppl所说,街道地址很凌乱。但是,如果您的地址数据代表美国地址,并且您有完整的地址(包括城市、州和/或邮政编码),您可以使用地址验证服务来解析(并验证!)和标准化这些组件。我在SmartyStreets工作,这是一个地址验证提供商。以下是我之前写的一个快速的C#示例,调用我们的LiveAddress API:

https://github.com/smartystreets/LiveAddressSamples/blob/master/c-sharp/street-address.cs

这是该示例的结果输出(请注意,街道名称和主要号码在“组件”部分中解析):
[
    {
        "input_index": 0,
        "candidate_index": 0,
        "delivery_line_1": "3214 N University Ave",
        "last_line": "Provo UT 84604-4405",
        "delivery_point_barcode": "846044405140",
        "components": {
            "primary_number": "3214",
            "street_predirection": "N",
            "street_name": "University",
            "street_suffix": "Ave",
            "city_name": "Provo",
            "state_abbreviation": "UT",
            "zipcode": "84604",
            "plus4_code": "4405",
            "delivery_point": "14",
            "delivery_point_check_digit": "0"
        },
        "metadata": {
            "record_type": "S",
            "county_fips": "49049",
            "county_name": "Utah",
            "carrier_route": "C016",
            "congressional_district": "03",
            "latitude": 40.27586,
            "longitude": -111.6576,
            "precision": "Zip9"
        },
        "analysis": {
            "dpv_match_code": "Y",
            "dpv_footnotes": "AABBR1",
            "dpv_cmra": "Y",
            "dpv_vacant": "N",
            "ews_match": false
        }
    }
]

这是一个解释所有字段的链接。

http://wiki.smartystreets.com/liveaddress_api_users_guide#json-responses

编辑:包括纬度/经度字段(新发布)。

目前只支持USPS地址。请查看www.worldaddresses.com或www.strikeiron.com以获取国际地址处理服务。 - Michael Whatcott
2
你可以尝试使用正则表达式,但是除非你能从地址标准化服务中获得一个标准化的地址对象,否则很难保证正确性。 - Dave Baghdanov

2
您需要更清晰地定义您正在寻找的模式,假设有一个模式存在。必须有一些常见的观察结果:

  • 街道地址由名称和数字组成。
  • 名称总是出现在数字之前。
  • 名称由一个或多个单词组成,用空格分隔。
  • 数字是一个数字,后面跟着一个可选字母。

从评论中可以看出,最后一点并不严格正确,因为街道号码的数字和字母部分可以由空格分隔。

如果您无法保证街道名称和数字的顺序,并且街道名称中的单词不包含数字,则我真的不确定任何东西能够帮助您。

以下正则表达式应该涵盖大多数情况:

Regex reggie = new Regex(@"^(?<name>\w[\s\w]+?)\s*(?<num>\d+\s*[a-z]?)$", RegexOptions.IgnoreCase)

使用\w是一个不好的主意。一些法国街道,如“Rue d'Alembert”将无法匹配等。 - Dyppl
然后你还需要考虑‘(’)’。 - Quick Joe Smith

0

这里假设你所有的“地址”都至少以上述方式之一进行格式化。

string address = "Streetname 1a"

string street = Regex.Replace(address, "^[^0-9]+", "");

string number = address.Replace(street, "");

然后修剪两个值。


有趣,如果我们能够排除数字,这可能有效。考虑到街道名称不包含任何数字。 - thedev
我已经更新了正则表达式,可能会起作用(即排除数字):) - Craigt
你知道有些街道的名称中包含数字,对吧?比如在纽约。 - Dyppl
是的,我明白这一点 :) 这就是为什么我说“这是假设你所有的“地址”都至少按照上述方式之一进行格式化。”此外,我认为每个人都意识到,鉴于地址可能有数百种不同的格式,这个问题是无法解决的。我只是提供了一个简单的解决方案,以防万一 OP 只需要涵盖他提供的示例格式。 - Craigt
@Craigt:好的,我没有恶意。 - Dyppl
@Dyppl - 没问题 :) ... @thedev ... 我已经更新了正则表达式,现在应该可以工作了吧? - Craigt

0

首先,您应该尝试使用 String.LastIndexOf() 在可能的位置进行拆分以查找数字。

然后,您应该检查此最后一组中是否有任何字符包含任何数字,例如 splittedValue.Any(c => Char.IsDigit(c));。因此,如果在此最后一组中找到任何数字,则可以相当确定您已正确拆分,但是可能存在不符合此行为的地址。

更新

如果您确实有这样的嘈杂数据需要规范化,我认为您不能做得比 @Dyppl 更好,并使用一些复杂的正则表达式,这些正则表达式必须通过示例进行演变,否则无法正常工作。


输入字符串的另一个可能性是:"街道名称1 a",在这种情况下,我也会得到错误的输出。 - thedev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接