Powerful Declarative Logic: Phone Number Parsing

When entering phone numbers in InfoPath, you can validate that it is a phone number easily enough (Data Validation->field1 “matches pattern” Phone number), but what do you do if the input does not match that pattern? Asking users to exactly enter the format “(000) 000-0000” may be a little constraining. Therefore, you may want a rule so that any combination of spaces, dashes, parenthesis and 10 digits will be reformatted nicely.  Below I will describe how to do this in a rule, although you could do the same in business logic. 

For the rule’s action, you would want to use translate, substring and concat functions.  Logically, as your first action you would translate to remove all unwanted characters (spaces, dashes and parenthesis).  Then, as your second action substring and concat everything. 

However, you cannot break it into two actions.  When the translate is executed, it changes the text field.  InfoPath immediately reruns the rule on that field (even before the rest of the actions are run from the first time).  For the second run of this rule, the conditions will all pass (as you would have a number like “0123456789” due to the translate that already happened), and the translate action would be run again.  This will happen repeatedly and causes a recursion error. 

Therefore, you would need to do this all in one action (the rule will be rerun here, but the conditions will not pass since it will match the phone number pattern).

On your text box, you will need:

1. One condition with 3 parts:

- Field “does not match pattern” phone number “and”

- Without the “()- “ characters, it has length 10:
“The expression” string-length(translate(., "()- ", "")) = 10 “and”

- There are no characters other than digits and “()- “ characters
“The expression” string-length(translate(translate(., "()- 0123456789", "")) = 0

2. One action that removes (), - and spaces; takes substrings; and concatenates it all together at once:

Set a field’s value “.” to concat("(",substring(translate(., "()-", ""), 1, 3), ")", substring(translate(., "()-", ""), 4, 3), "-", substring(translate(., "()-", ""), 7, 4)))

And you're done! Note that this technique will work in InfoPath 2003 and 2007, and it is supported in browser-enabled form templates. Download the form template that has this trick implemented; make sure to save it to your desktop.

Nicholas Lovell
Software Design Engineer
Alexei Levenkov
Software Design Engineer


Comments (10)

  1. davidacoder says:

    But you won’t be able to input international phone numbers, right? If yes, that would be a terrible solution to implement in any place, because I just can’t imagine any software that stores phone numbers where there isn’t eventually a need to store internation phone numbers…

  2. nilovell says:

    Thank you for your comments David.  

    You are correct that this will not handle international phone numbers.  Successfully parsing the entire set of phone numbers from every country turns out to be an incredibly difficult problem using this approach since the number of digits in the phone numbers varies by country and could change at any moment.  

    This is intended to be a sample of how to use substring and translate, which happens to parse phone numbers from the United States.  An alternate approach to handling generic phone numbers would be to use business logic.

  3. davidacoder says:

    I agree, just validating international phone numbers is incredbly difficult. But at the end of the day, it is in almost all cases better to NOT validate at all than to be too restrictive in your validation. I just fear that blog posts like this will lead to more forms or apps with too restrictive validation logic.

    Let me just repeat: I think no one should reuse this example here in a real world app. I cannot imagine any scenario where you can rule out during dev time that someone will at some point want to enter an international phone number in your phone number field.

    I think it would be great if you would augment this post with a warning "Never use this in the real world, you will annoy your users to no end if you disallow entering international phone numbers".

  4. nilovell says:

    The example provided here does not do any data validation.  Therefore, it is not restrictive, and the form user can enter any phone number that he or she wants (international and otherwise).  It just so happens that if you enter 10 digits with parenthesis, spaces and dashes, it will be reformatted to look like the USA format for phone numbers.  

    For an international phone number, you would use ‘+’ and the country code at the beginning (+44 for the UK for example).  Using the + symbol means that this rule will not run (since it does not recognize it), and therefore, international phone numbers are, in fact, allowed.  

  5. davidacoder says:

    Sorry, I should have tried it before starting to complain. If it doesn’t validate, it actually looks great to me!

  6. dave bach says:

    The real issue is that not all validation using regular expressions is acceptable by InfoPath.

    I use regexlib.com patterns for years in web apps. But the following is not acceptable according to InfoPath 2007.

    (?:([2-9]d{2}) ?|[2-9]d{2}(?:-?| ?))[2-9]d{2}[- ]?d{4}

    This regular expression will allow three US formats with  area codes 200-999:

    5305551212 or (530) 555-1212 or 530-555-1212

    I had to fall back to the following which still allows the following format, 530-555-1212 with area codes 200-999.

    [2-9]d{2}-d{3}-d{4} is accepted by InfoPath 2007.

    In order to provide richer validation and support for international phones, InfoPath 2007 needs to support these regular expressions.


  7. mehtavipulk says:


    I have a requirement where in I do not want user to enter charecters other than Numbers, -, (,)

    Using what expression do I need to implement this procedure?


    Vipul Mehta

  8. Peggy Bright says:

    Too bad the form template is no longer available.

  9. Mel C. says:

    In the third rule for step 1, you typed the word 'translate' twice. As the screenshot and in the sample form, it should read " string-length(translate(., "()- 0123456789", "")) = 0 "

  10. blksheep says:

    This works like a charm with one exception… Now the placeholder is missing and in its place "()-" waiting for data.

    How do I maintain the placeholder, or simply have nothing in the entry space?

Skip to main content