开发者

Regex to match address with subpatterns

开发者 https://www.devze.com 2023-04-03 11:52 出处:网络
I am attempting to create a regular expression to parse an address into five parts: "address1", which is the street address, "address2", which is the apartment number or whatever e

I am attempting to create a regular expression to parse an address into five parts: "address1", which is the street address, "address2", which is the apartment number or whatever else shows up on line 2 of an address, the city, state, and zip code.

When I run this, Python (or Django) is throwing an error which states "unexpected end of pattern" when I run re.search. Can anyone tell me how to modify this regular expression to match correctly?

I'm very much a regular expression noob. I can make out most of what this one is supposed to do, but I could never have written it myself. I got this from http://regexlib.com/REDetails.aspx?regexp_id=472.

re.compile(r"""
(?x)^(?n:
(?<address1>
    (\d{1,5}(\ 1\/[234])?(\x20[A-Z]([a-z])+)+ )
    | (P.O. Box \d{1,5}))\s{1,2}
(?<city>
    [A-Z]([a-z]) 
        + (\.?)(\x20[A-Z]([a-z])+){0, 2})\, \x20
(?<state>
    A[LKSZRAP] | C[AOT] | D[EC] | F[LM] | G[AU] | HI
    | I[ADL N] | K[SY] | LA | M[ADEHINOPST] | N[CDEHJMVY]
    | O[HKR] | P[ARW] | RI | S[CD] | T[NX] | UT | V[AIT] 
    | W[AIVY] 
    | [A-Z]([a-z])
        + (\.?)(\x20[A-Z]([a-z])+){0,2})\x20
(?<zipcode>
    (?!0{5})\d{5}(-\d {4})?)
)$"
""", re.VERBOSE)

Newlines added for readability. As a follow-up question, can this regex be separated into multiple lines like this for readability, or will it need to be all in one line to work (I could just concatenate the separate lines, I suppose)?

P.S. I know this smells like homework, but it is actually for work.

Edit: Actual code being used was requested, so here it is. I left it out because everything here is actually already up there, but perhaps it will help.

The function is part of a Django view, but that shouldn't matter too much for our purposes.

def parseAddress(address):
  pattern = r"^(?n:(?<address1>(\开发者_开发问答d{1,5}(\ 1\/[234])?(\x20[A-Z]([a-z])+)+ )|(P\.O\.\ Box\ \d{1,5}))\s{1,2}(?i:(?<address2>(((APT|APARTMENT|BLDG|BUILDING|DEPT|DEPARTMENT|FL|FLOOR|HNGR|HANGER|LOT|PIER|RM|ROOM|S(LIP|PC|T(E|OP))|TRLR|TRAILER|UNIT)\x20\w{1,5})|(BSMT|BASEMENT|FRNT|FRONT|LBBY|LOBBY|LOWR|LOWER|OFC|OFFICE|PH|REAR|SIDE|UPPR|UPPER)\.?)\s{1,2})?)(?<city>[A-Z]([a-z])+(\.?)(\x20[A-Z]([a-z])+){0,2})\, \x20(?<state>A[LKSZRAP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADL N]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD] |T[NX]|UT|V[AIT]|W[AIVY]|[A-Z]([a-z])+(\.?)(\x20[A-Z]([a-z])+){0,2})\x20(?<zipcode>(?!0{5})\d{5}(-\d {4})?))$"
  match = re.search(pattern, address)

I was using my home address as the input, but I tried "123 Main St., Austin, TX 12345" as input as well with the same result.


Some people might not consider this an answer, but bear with me for a minute.

I HIGHLY recommend AGAINST trying to parse street addresses with a regex. Street addresses are not "regular" in any sense of the word. There is infinite variation, and unless you restrict yourself to a very limited grammar, there will always be strings you cannot parse. A huge amount of time and money has been invested in solutions to parse addresses, starting with the US Post Office and the many, many providers of list cleanup services. Just Google "parsing street addresses" to get a hint of the scope of the problem. There are commercial solutions and some free solutions, but the comments on the web indicate that nobody gets it right all the time.

I also speak from experience. During the '80s I worked for a database typesetting company, and we had to parse addresses. We never were able to develop a solution that worked perfectly, and for data we captured ourselves (we had a large keyboarding department) we developed a special notation syntax so the operators could insert delimiters at the appropriate locations to help the parsing process.

Take a look at some of the free services out there. You will save yourself a lot of hassle.


Set x (verbose) flag in regex, i.e.: (?x)


a non-regex answer: check out the python library usaddress (there's also a web interface for trying it out)

agree w/ Jim that regex isn't a good solution here. usaddress parses addresses probabilistically, and is much more robust than regex-based parsers when dealing with messy addresses.


Your regex fails on the first character n, which you can verify as follows. Make a file test.py and put the following:

 import re
 re.compile(r'...')

where you fill in your pattern of course :) Now run python -m pdb test.py, enter c to continue and it will stop when the exception is raised. At that point type l to see where in the code you are. You see it fails because source.next isn't in FLAGS. This source is just your pattern, so you verify where it fails by typing print source.index.

Furthermore, removing that n in front, the pattern fails at the first a of <address1>.

The (?n is strange, I can't find it in the documentation, so it seems to be an unsupported extension. As for the ?<address1>, I think this should be ?P<address1>. There are more problems with it, like (?i: and if I remove those and fix the ?P< stuff, I get an error about unbalanced parenthesis at the last parenthesis.


Jim Garrison (above) is correct - addresses are too varied to parse with a regex. I work for an address verification software company - SmartyStreets. Try out our LiveAddress API - the REST endpoint provides all the address components parsed in a nice, easy to use JSON response. Here's a sample:

https://github.com/smartystreets/LiveAddressSamples/blob/master/python/street-address.py

0

精彩评论

暂无评论...
验证码 换一张
取 消