¿Cuál es la mejor manera de convertir números de teléfono a formato internacional (E.164) usando Java?

StackOverflow https://stackoverflow.com/questions/187216

Pregunta

¿Cuál es la mejor manera de convertir números de teléfono a formato internacional (E.164) usando Java?

Dado un 'número de teléfono' y una identificación de país (digamos un código de país ISO), me gustaría convertirlo en un número de teléfono de formato internacional estándar E.164.

Estoy seguro de que puedo hacerlo a mano con bastante facilidad, pero no estaría seguro de que funcionaría correctamente en todas las situaciones.

¿Qué framework / biblioteca / utilidad de Java recomendaría para lograr esto?

P.S. El "número de teléfono" podría ser cualquier cosa identificable por el público en general, como

* (510) 786-0404
* 1-800-GOT-MILK
* +44-(0)800-7310658

ese último es mi favorito: así es como algunas personas escriben su número en el Reino Unido y significa que debe usar el +44 o el 0.

El número de formato E.164 debe ser todo numérico y utilizar el código de país internacional completo (por ejemplo, + 44)

¿Fue útil?

Solución

Google proporciona una biblioteca para trabajar con números de teléfono. El mismo que usan para Android

http://code.google.com/p/libphonenumber/

String swissNumberStr = "044 668 18 00"
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
try {
  PhoneNumber swissNumberProto = phoneUtil.parse(swissNumberStr, "CH");
} catch (NumberParseException e) {
  System.err.println("NumberParseException was thrown: " + e.toString());
}

// Produces "+41 44 668 18 00"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.INTERNATIONAL));
// Produces "044 668 18 00"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.NATIONAL));
// Produces "+41446681800"
System.out.println(phoneUtil.format(swissNumberProto, PhoneNumberFormat.E164));

Otros consejos

Hablando de la experiencia en escribir este tipo de cosas, es realmente difícil hacerlo con 100% de confiabilidad. He escrito un código Java para hacer esto que es razonablemente bueno para procesar los datos que tenemos, pero que no será aplicable en todos los países. Las preguntas que debe hacer son:

¿Son consistentes las asignaciones de caracteres a números entre países? Estados Unidos usa mucho de esto (por ejemplo, 1800-GOT-MILK) pero en Australia, como ejemplo, es bastante raro. Lo que debe hacer es asegurarse de que está haciendo la asignación correcta para el país en cuestión si varía (puede que no). No sé qué hacen los países que usan diferentes alfabetos (por ejemplo, cirílico en Rusia y los países del antiguo bloque oriental);

Debe aceptar que su solución no será 100% y no debe esperar que lo sea. Debes hacer una "mejor conjetura" enfoque. Por ejemplo, no existe una forma real de saber que 132345 es un número de teléfono válido en Australia, al igual que 1300 123 456, pero que estos son los dos únicos patrones que son para números 13xx y no se pueden llamar desde el extranjero;

También debe preguntar si desea validar regiones (códigos de área). Creo que los EE. UU. Usan un sistema en el que el segundo dígito del código de área es 1 o 0. Esto puede haber sido el caso una vez, pero no estoy seguro de si aún se aplica. Cualquiera sea el caso, muchos otros países tendrán otras reglas. En Australia, los códigos de área válidos para teléfonos fijos y móviles (celulares) tienen dos dígitos (el primero es 0). 08, 03 y 04 son todos válidos. 01 no lo es. ¿Cómo se ocupa de eso? ¿Quieres?

Los países usan diferentes convenciones sin importar cuántos dígitos estén escribiendo. Debe decidir si desea aceptar algo distinto de la "norma". Todos estos son comunes en Australia:

  • (02) 1234 5678
  • 02 1234 5678
  • 0411 123 123 (pero nunca he visto 04 1112 3456)
  • 131 123
  • 13 1123
  • 131 123
  • 1 300 123 123
  • 1300 123 123
  • 02-1234-5678
  • 1300-234-234
  • +44 78 1234 1234
  • +44 (0) 78 1234 1234
  • + 44-78-1234-1234
  • + 44- (0) 78-1234-1234
  • 0011 44 ??78 1234 1234 (0011 es el código de marcación internacional estándar)
  • (44) 078 1234 1234 (no común)

Y eso está justo en la parte superior de mi cabeza. Por un pais. En Francia, por ejemplo, es común escribir el número de teléfono en pares de números (12 34 56 78) y también lo pronuncian de esa manera: en lugar de:

un (uno), deux (dos), trois (tres), ...

su

douze (doce), trente-quatre (treinta y cuatro), ...

¿Desea satisfacer ese nivel de diferencia cultural? Supongo que no, pero vale la pena considerar la pregunta en caso de que haga que sus reglas sean demasiado estrictas.

También algunas personas pueden agregar números de extensión en los números de teléfono, posiblemente con " ext " o abreviatura similar. ¿Quieres atender eso?

Lo siento, no hay código aquí. Solo una lista de preguntas que debe hacerse y cuestiones a considerar. Como han dicho otros, una serie de expresiones regulares puede hacer mucho de lo anterior, pero en última instancia, los campos de números de teléfono son (en su mayoría) texto de forma libre al final del día.

Esta fue mi solución:

public static String FixPhoneNumber(Context ctx, String rawNumber)
{
    String      fixedNumber = "";

    // get current location iso code
    TelephonyManager    telMgr = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
    String              curLocale = telMgr.getNetworkCountryIso().toUpperCase();

    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    Phonenumber.PhoneNumber     phoneNumberProto;

    // gets the international dialling code for our current location
    String              curDCode = String.format("%d", phoneUtil.getCountryCodeForRegion(curLocale));
    String              ourDCode = "";

    if(rawNumber.indexOf("+") == 0)
    {
        int     bIndex = rawNumber.indexOf("(");
        int     hIndex = rawNumber.indexOf("-");
        int     eIndex = rawNumber.indexOf(" ");

        if(bIndex != -1)
        {
            ourDCode = rawNumber.substring(1, bIndex);
        }
        else if(hIndex != -1) 
        {               
            ourDCode = rawNumber.substring(1, hIndex);
        }
        else if(eIndex != -1)
        {
            ourDCode = rawNumber.substring(1, eIndex);
        }
        else
        {
            ourDCode = curDCode;
        }           
    }
    else
    {
        ourDCode = curDCode;
    }

    try 
    {
      phoneNumberProto = phoneUtil.parse(rawNumber, curLocale);
    } 

    catch (NumberParseException e) 
    {
      return rawNumber;
    }

    if(curDCode.compareTo(ourDCode) == 0)
        fixedNumber = phoneUtil.format(phoneNumberProto, PhoneNumberFormat.NATIONAL);
    else
        fixedNumber = phoneUtil.format(phoneNumberProto, PhoneNumberFormat.INTERNATIONAL);

    return fixedNumber.replace(" ", "");
}

Espero que esto ayude a alguien con el mismo problema.

Disfruta y usa libremente.

Gracias por las respuestas. Como se indicó en la pregunta original, estoy mucho más interesado en formatear el número en el formato estándar que en determinar si es un número de teléfono válido (como en el original).

Tengo un código hecho a mano actualmente que toma una cadena de número de teléfono (según lo ingresado por el usuario) y un contexto de país de origen y contexto de país de destino (el país desde donde se marca el número y el país donde el número se está marcando, esto es conocido por el sistema) y luego realiza la siguiente conversión en pasos

  1. Elimina todos los espacios en blanco del número

  2. Traduce todos los alfa a dígitos, usando una tabla de búsqueda de letra a dígito (por ejemplo, A - > 2, B - > 2, C - > 2, D - > 3) etc. para el teclado (no sabía que algunos teclados los distribuían de manera diferente)

  3. Elimine todos los signos de puntuación, manteniendo intacto un '+' anterior si existe (en caso de que el número ya esté en algún tipo de formato internacional).

  4. Determine si el número tiene un prefijo de marcación internacional para el contexto del país, p. ej. si el contexto de origen es el Reino Unido, vería si comienza con un '00' y lo reemplaza con un '+'. Actualmente no verifico si los dígitos que siguen al '00' van seguidos del código de marcación internacional para el país de destino. Busco el prefijo de marcación internacional para el país de origen en una tabla de búsqueda (por ejemplo, GB - > '00 ', EE. UU. & & Gt;' 011 'etc.)

  5. Determine si el número tiene un prefijo de marcación local para el contexto del país, p. ej. si el contexto de origen es el Reino Unido, me gustaría ver si comienza con un '0' y reemplazarlo con un '+' seguido del código de marcación internacional para el país de destino. Busco el prefijo de marcación local para el país de origen en una tabla de búsqueda (por ejemplo, GB - > '0', EE. UU. & & Gt; '1' etc.), y el código de marcación internacional para el país de destino en otra búsqueda tabla (por ejemplo, 'GB' = '44', US = '1')

Parece funcionar para todo lo que le he arrojado hasta ahora, a excepción de la situación +44 (0) 1234-567-890, agregaré una verificación de caso especial para eso.

Escribir no fue difícil, y puedo agregar casos especiales para cada extraña excepción que encuentro. Pero realmente me gustaría saber si hay una solución estándar.

Las compañías telefónicas parecen lidiar con esto todos los días. Nunca obtengo resultados inconsistentes al marcar números usando la PSTN. Por ejemplo, en los EE. UU. (Donde los teléfonos móviles tienen los mismos códigos de área que los teléfonos fijos, podría marcar + 1-123-456-7890 o 011-1-123-456-7890 (donde 011 es el prefijo de marcación internacional en el EE. UU. Y 1 es el código de marcación internacional para EE. UU.), 1-123-456-7890 (donde 1 es el prefijo de marcación local en EE. UU.) O incluso 456-7890 (suponiendo que estaba en el código de área 123 en ese momento) y obtengo los mismos resultados cada vez. Supongo que internamente estos números marcados se convierten al mismo formato estándar E.164, y que la conversión se realiza en el software.

Para ser honesto, parece que ya tienes la mayoría de las bases cubiertas.

El formato +44 (0) 800 a veces (incorrectamente) utilizado en el Reino Unido es molesto y no es estrictamente válido de acuerdo con E.123, que es la recomendación del UIT-T sobre cómo se deben mostrar los números. Si no tiene una copia de E.123, vale la pena echarle un vistazo.

Por lo que vale, la red telefónica en sí misma no siempre usa E.164. A menudo habrá una bandera en la señalización ISDN generada por la PBX (o en la red si está usando un teléfono de vapor) que le indica a la red si el número marcado es local, nacional o internacional.

Esta es una tarea muy difícil ya que los números de teléfono se escriben de manera diferente en casi cada país.

Solíamos mantener una lista de REGEXP (admitimos 19 formatos) para analizar 3 partes de un número y luego convertimos esas 3 partes a " + {1} {2} {3} " ;.

Ordene las expresiones regulares primero por más específicas y luego tome la primera que tenga éxito en el análisis.

En algunos países, puede validar el 112 como un número de teléfono válido, pero si coloca un código de país delante ya no será válido. En otros países, no puede validar el 112 pero puede validar el 911 como un número de teléfono válido.

He visto algunos teléfonos que ponen Q en la tecla 7 y Z en la tecla 9. He visto algunos teléfonos que ponen Q y Z en la tecla 0, y algunos que ponen Q y Z en la tecla 1.

Un código de área que existía ayer podría no existir hoy, y viceversa.

En la mitad de América del Norte (código de país 1), la regla del segundo dígito solía ser 0 o 1 para los códigos de área, pero esa regla desapareció hace 10 años.

No conozco una biblioteca o marco estándar disponible para formatear números de teléfono en E.164.

La solución utilizada para nuestro producto, que requiere formatear el identificador de llamadas proporcionado por PBX en E.164, es implementar un archivo (tabla de base de datos) que contenga la información de formato E.164 para todos los países aplicables. Esto tiene la ventaja de que la aplicación se puede actualizar (para manejar todos los casos de esquina extraños en varias redes PSTN) sin requerir cambios en la base del código de producción.

La tabla contiene una fila para cada código de país e información sobre la longitud del código de área y la longitud del suscriptor. Puede haber múltiples entradas para un país dependiendo de las variaciones posibles con el código de área y la longitud del número de suscriptor.

Uso del plan de marcado PSTN (parcial) de Nueva Zelanda como ejemplo de la tabla ...

CC  AREA_CODE  AREA_CODE_LENGTH  SUBSCRIBER  SUBSCRIBER_LENGTH
64                            1              7
64         21                 2              7
64        275                 3              6

Hacemos algo similar a lo que usted ha descrito, es decir, quitamos el número de teléfono proporcionado de cualquier carácter que no sea un dígito y luego formateamos según varias reglas con respecto a la longitud total del plan de números, código de acceso externo y códigos de acceso de larga distancia / internacional.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top