Search code examples
pythonregexmobile-websitephone-number

US-format phone numbers to links in Python


I'm working a piece of code to turn phone numbers into links for mobile phone - I've got it but it feels really dirty.

import re
from string import digits

PHONE_RE = re.compile('([(]{0,1}[2-9]\d{2}[)]{0,1}[-_. ]{0,1}[2-9]\d{2}[-_. ]{0,1}\d{4})')

def numbers2links(s):
    result = ""
    last_match_index = 0
    for match in PHONE_RE.finditer(s):
          raw_number = match.group()
          number = ''.join(d for d in raw_number if d in digits)
          call = '<a href="tel:%s">%s</a>' % (number, raw_number)
          result += s[last_match_index:match.start()] + call
          last_match_index = match.end()
    result += s[last_match_index:]
    return result

>>> numbers2links("Ghost Busters at (555) 423-2368! How about this one: 555 456 7890! 555-456-7893 is where its at.")
'Ghost Busters at <a href="tel:5554232368">(555) 423-2368</a>! How about this one: <a href="tel:5554567890">555 456 7890</a>! <a href="tel:5554567893">555-456-7893</a> is where its at.'

Is there anyway I could restructure the regex or the the regex method I'm using to make this cleaner?

Update

To clarify, my question is not about the correctness of my regex - I realize that it's limited. Instead I'm wondering if anyone had any comments on the method of substiting in links for the phone numbers - is there anyway I could use re.replace or something like that instead of the string hackery that I have?


Solution

  • Nice first take :) I think this version is a bit more readable (and probably a teensy bit faster). The key thing to note here is the use of re.sub. Keeps us away from the nasty match indexes...

    import re
    
    PHONE_RE = re.compile('([(]{0,1}[2-9]\d{2}[)]{0,1}[-_. ]{0,1}[2-9]\d{2}[-_.  ]{0,1}\d{4})')
    NON_NUMERIC = re.compile('\D')
    
    def numbers2links(s):
    
       def makelink(mo):
          raw_number = mo.group()
          number = NON_NUMERIC.sub("", raw_number)
          return '<a href="tel:%s">%s</a>' % (number, raw_number)
    
       return PHONE_RE.sub(makelink, s)
    
    
    print numbers2links("Ghost Busters at (555) 423-2368! How about this one: 555 456 7890! 555-456-7893 is where its at.")
    

    A note: In my practice, I've not noticed much of a speedup pre-compiling simple regular expressions like the two I'm using, even if you're using them thousands of times. The re module may have some sort of internal caching - didn't bother to read the source and check.

    Also, I replaced your method of checking each character to see if it's in string.digits with another re.sub() because I think my version is more readable, not because I'm certain it performs better (although it might).