Blog alskare

.Net y lo que surja

Validar NIF, NIE, CIF

Posted by alskare en 21/07/2009

En el post anterior ( Validar número de cuenta bancaria española ) estuve creando un método que permitía validar una cuenta bancaria con la finalidad de poder informar al usuario en el caso que introduzca mal alguno de los 20 dígitos numéricos de dicho elemento.

Siguiendo con el apartado de las validaciones, le toca esta vez a los Códigos de identificación fiscal. Y no es que me aburra ahora, lo que pasa es que me he ido dando cuenta que, con el paso del tiempo, una simple comprobación de algún NIF al azar de las bases de datos, ofrece un alto porcentaje de error. Así, con más necesidad que ganas, me dispongo a la creación de algún tipo de servicio que me permita realizar una validación del famoso NIF.

Nada más empezar, una simple búsqueda genérica por San Google (Wikipedia – Nif), empiezo a entender que no puedo crear la típica función que devuelva un valor booleano si está bien o no el número y su correspondiente letra, entre otras cosas porque me gustaría que, además de saber si el número introducido es correcto, también me gustaría obtener un formato homogéneo para poder almacenarlo en la base de datos.

public static bool ValidaNumeroNif(string nif)
{
    //...
}

De esta manera, cuando empiezo a tener claro que voy a devolver una clase, me pongo manos a la obra y, además de obtener si es correcto o no, pues devuelvo también varias cosas que, aunque dudo de su utilidad, como las he necesitado para los cálculos, las he incluido.

Incluyo una captura de los métodos públicos de la clase, destacando:

  • Método estático CompruebaNif, que realmente es el encargado de realizar todo el trabajo, averiguando si se trata de un NIF, NIE, o CIF (de empresa), además de desglosar la información y comprobar que realmente sea correcto.
  • Propiedad booleana EsCorrecto, que  nos indicará si el Nif introducido está validado correctamente
  • Método ToString(), que permitirá obtener un formato homogéneo para poder almacenar en la base de datos. Podéis fijaros que el resultado de este método es muy fácil de modificar al gusto de cada uno.

image

Bueno, aunque todavía me faltaría algún tipo de comprobación, sobre todo con los códigos intracomunitarios, por lo que he estado viendo, funciona bastante bien. Supongo que se podría añadir algún que otro complemento, como por ejemplo, que se pudiese obtener el tipo de sociedad o la provincia en la que se dio de alta en la Seguridad Social, pero no creo que tuviese mucha utilidad salvo para nosotros mismos como programadores.

Una de las cosas que me ha llamado la atención es que nunca había hecho uso de las Expresiones Regulares y realmente he visto que tienen una gran utilidad a la vez que te evitan muchas líneas de código (¿podéis imaginar cómo hice la primera versión del método GetTipoDocumento?…  jeje). Por lo demás, la verdad es que ya me voy acostumbrando a la sintaxis del C#.

Pues nada, confío que lo disfrutéis. Yo me he divertido bastante haciéndolo.

using System;
using System.Text.RegularExpressions;

/*
 * NumeroNif.cs
 * Servicios de validación de los números NIF
 * 
 * älskare, Jul/09
 */

namespace jnSoftware.Validaciones
{

    /// <summary>
    /// Representa un número. En la clase se desglosan las distintas opciones que se puedan
    /// encontrar
    /// </summary>
    public class NumeroNif
    {
        /// <summary>
        /// Tipos de Códigos.
        /// </summary>
        /// <remarks>Aunque actualmente no se utilice el término CIF, se usa en la enumeración
        /// por comodidad</remarks>
        private enum TiposCodigosEnum { NIF, NIE, CIF }

        // Número tal cual lo introduce el usuario
        private string numero;
        private TiposCodigosEnum tipo;

        /// <summary>
        /// Parte de Nif: En caso de ser un Nif intracomunitario, permite obtener el cógido del país
        /// </summary>
        public string CodigoIntracomunitario { get; internal set; }
        internal bool EsIntraComunitario { get; set; }

        /// <summary>
        /// Parte de Nif: Letra inicial del Nif, en caso de tenerla
        /// </summary>
        public string LetraInicial { get; internal set; }

        /// <summary>
        /// Parte de Nif: Bloque numérico del NIF. En el caso de un NIF de persona física,
        /// corresponderá al DNI
        /// </summary>
        public int Numero { get; internal set; }

        /// <summary>
        /// Parte de Nif: Dígito de control. Puede ser número o letra
        /// </summary>
        public string DigitoControl { get; internal set; }

        /// <summary>
        /// Valor que representa si el Nif introducido es correcto
        /// </summary>
        public bool EsCorrecto { get; internal set; }

        /// <summary>
        /// Cadena que representa el tipo de Nif comprobado:
        ///     - NIF : Número de identificación fiscal de persona física
        ///     - NIE : Número de identificación fiscal extranjería
        ///     - CIF : Código de identificación fiscal (Entidad jurídica)
        /// </summary>
        public string TipoNif { get { return tipo.ToString(); } }

        /// <summary>
        /// Constructor. Al instanciar la clase se realizan todos los cálculos
        /// </summary>
        /// <param name="numero">Cadena de 9 u 11 caracteres que contiene el DNI/NIF
        /// tal cual lo ha introducido el usuario para su verificación</param>
        private NumeroNif(string numero)
        {
            // Se eliminan los carácteres sobrantes
            numero = EliminaCaracteres(numero);

            // Todo en maýusculas
            numero = numero.ToUpper();

            // Comprobación básica de la cadena introducida por el usuario
            if (numero.Length != 9 && numero.Length != 11)
                throw new ArgumentException("El NIF no tiene un número de caracteres válidos");

            this.numero = numero;
            Desglosa();

            switch (tipo )
            {
                case TiposCodigosEnum.NIF:
                case TiposCodigosEnum.NIE:
                    this.EsCorrecto = CompruebaNif();
                    break;
                case TiposCodigosEnum.CIF:
                    this.EsCorrecto = CompruebaCif() ;
                    break;
            }
        }

        #region Preparación del número (desglose)

        /// <summary>
        /// Realiza un desglose del número introducido por el usuario en las propiedades
        /// de la clase
        /// </summary>
        private void Desglosa()
        {
            Int32 n;
            if (numero.Length == 11)
            {
                // Nif Intracomunitario
                EsIntraComunitario = true;
                CodigoIntracomunitario = numero.Substring(0, 2);
                LetraInicial = numero.Substring(2, 1);
                Int32.TryParse(numero.Substring(3, 7), out n);
                DigitoControl = numero.Substring(10, 1);
                tipo = GetTipoDocumento(LetraInicial[0]);
            }
            else
            {
                // Nif español
                tipo = GetTipoDocumento(numero[0]);
                EsIntraComunitario = false;
                if (tipo == TiposCodigosEnum.NIF)
                {
                    LetraInicial = string.Empty;
                    Int32.TryParse(numero.Substring(0, 8), out n);
                }
                else
                {
                    LetraInicial = numero.Substring(0, 1);
                    Int32.TryParse( numero.Substring(1, 7),out  n);
                }
                DigitoControl = numero.Substring(8, 1);
            }
            Numero = n;
        }

             /// <summary>
        /// En base al primer carácter del código, se obtiene el tipo de documento que se intenta
        /// comprobar
        /// </summary>
        /// <param name="letra">Primer carácter del número pasado</param>
        /// <returns>Tipo de documento</returns>
        private TiposCodigosEnum GetTipoDocumento(char letra)
        {
            Regex regexNumeros = new Regex("[0-9]");
            if ( regexNumeros.IsMatch(letra.ToString()) )
                return TiposCodigosEnum.NIF;

            Regex regexLetrasNIE = new Regex("[LKXYM]");
            if ( regexLetrasNIE.IsMatch(letra.ToString()) )
                return TiposCodigosEnum.NIE ;

            Regex regexLetrasCIF = new Regex("[ABCDEFGHJPQRSUVNW]");
            if ( regexLetrasCIF.IsMatch(letra.ToString()) )
                return TiposCodigosEnum.CIF;

            throw new ApplicationException("El código no es reconocible");
        }

        /// <summary>
        /// Eliminación de todos los carácteres no numéricos o de texto de la cadena
        /// </summary>
        /// <param name="numero">Número tal cual lo escribe el usuario</param>
        /// <returns>Cadena de 9 u 11 carácteres sin signos</returns>
        private string EliminaCaracteres(string numero)
        {
            // Todos los carácteres que no sean números o letras
            string caracteres = @"[^\w]";
            Regex regex = new Regex(caracteres);
            return regex.Replace(numero, "");
        }

        #endregion 

        #region Cálculos 

        private bool CompruebaNif()
        {
            return DigitoControl==GetLetraNif();
        }

        /// <summary>
        /// Cálculos para la comprobación del Cif (Entidad jurídica)
        /// </summary>
        private bool CompruebaCif()
        {
            string[] letrasCodigo = {"J", "A", "B", "C", "D", "E", "F", "G", "H", "I" };

            string n = Numero.ToString();
            Int32 sumaPares = 0;
            Int32 sumaImpares = 0;
            Int32 sumaTotal = 0;
            Int32 i = 0;
            string digitoCalculado;
            bool retVal = false;

            // Recorrido por todos los dígitos del número
            for (i = 0; i < n.Length; i++)
            {
                Int32 aux;
                Int32.TryParse(n[i].ToString(), out aux);

                if ((i+1) % 2 == 0)
                {
                    // Si es una posición par, se suman los dígitos
                    sumaPares += aux;
                }
                else
                {
                    // Si es una posición impar, se multiplican los dígitos por 2 
                    aux = aux * 2;

                    // se suman los dígitos de la suma
                    sumaImpares += SumaDigitos(aux);
                }
            }
            // Se suman los resultados de los números pares e impares
            sumaTotal += sumaPares + sumaImpares;

            // Se obtiene el dígito de las unidades
            Int32 unidades = sumaTotal % 10;

            // Si las unidades son distintas de 0, se restan de 10
            if (unidades != 0)
                unidades = 10 - unidades;

            switch (LetraInicial)
            {
                    // Sólo números
                case "A":
                case "B":
                case "E":
                case "H":
                    retVal = DigitoControl == unidades.ToString();
                    break;

                    // Sólo letras
                case "K":
                case "P":
                case "Q":
                case "S":
                    retVal = DigitoControl == letrasCodigo[unidades];
                    break;

                default:
                    retVal = (DigitoControl == unidades.ToString())
                            || (DigitoControl == letrasCodigo[unidades]);
                    break;
            }

            return retVal ;

        }

        /// <summary>
        /// Obtiene la suma de todos los dígitos
        /// </summary>
        /// <returns>de 23, devuelve la suma de 2 + 3</returns>
        private Int32 SumaDigitos(Int32 digitos)
        {
            string sNumero = digitos.ToString();
            Int32 suma = 0;

            for (Int32 i = 0; i < sNumero.Length; i++)
            {
                Int32 aux;
                Int32.TryParse(sNumero[i].ToString(), out aux);
                suma += aux;
            }
            return suma;
        }

        /// <summary>
        /// Obtiene la letra correspondiente al Dni
        /// </summary>
        private string GetLetraNif()
        {
            int indice = Numero % 23;
            return "TRWAGMYFPDXBNJZSQVHLCKET"[indice].ToString();
        }

        /// <summary>
        /// Obtiene una cadena con el número de identificación completo
        /// </summary>
        public override string ToString()
        {
            string nif;
            nif = EsIntraComunitario ? CodigoIntracomunitario :
                string.Empty + LetraInicial + Numero + DigitoControl;
            return nif;
        }

        #endregion

        /// <summary>
        /// Comprobación de un número de identificación fiscal español
        /// </summary>
        /// <param name="numero">Numero a analizar</param>
        /// <returns>Instancia de <see cref="NumeroNif"/> con los datos del número.
        /// Destacable la propiedad <seealso cref="NumeroNif.EsCorrecto"/>, que contiene la verificación
        /// </returns>
        public static NumeroNif CompruebaNif(string numero)
        {

            return new NumeroNif(numero);
        }

    }

}

Si te ha interesado el código, puedes probarlo en la siguiente dirección: http://www.velasco.biz/html/desarrollo/Validaciones.aspx

Observaciones: Gracias a Javier García que se dio cuenta de un error que tenía en el método CompruebaCif y me lo comunicó. De paso, se ha añadido la posibilidad de descarga de la librería: Descarga

27 comentarios to “Validar NIF, NIE, CIF”

  1. […] de tener un “formato igual”. En el blog anterior ya quedó alguno de estos intentos. Una de las rutinas que más me costó en su día fue el intento de poder validar, en un mismo campo, un CIF de […]

  2. […] https://alskare.wordpress.com, un día se me ocurrió hacer una rutina que permitiese validar NIF, NIE y CIF . Sí, todo a la vez, así evitamos al usuario que tenga que introducir el tipo de […]

  3. […] blog anterior, https://alskare.wordpress.com, un día se me ocurrió hacer una rutina que permitiese validar NIF, NIE y CIF . Sí, todo a la vez, así evitamos al usuario que tenga que introducir el tipo de documento. Por […]

  4. Miguel said

    Modificacion por si acaso el NIE empieza y termina por la misma letra
    LetraInicial = numero.Substring(0, 1);

    switch (LetraInicial)
    {
    case “X”: numero = “0” + numero.Substring(1, 8); break;
    case “Y”: numero = “1” + numero.Substring(1, 8); break;
    case “Z”: numero = “2” + numero.Substring(1, 8); break;
    }

    Int32.TryParse(numero.Substring(0, 8), out n);

    Modificacion por si acaso el Documento empieza por mas de un 0

    if (n.Length < 7)
    {
    n = "0000000" + n;
    n = n.Substring(n.Length – 7, 7);
    }

  5. Miguel said

    Si el numero del CIF empieza por 0 no valida bien.
    Añadir tras :
    private bool CompruebaCif()
    {
    string[] letrasCodigo = { “J”, “A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I” };

    string n = Numero.ToString();

    if (n.Length < 7)
    {
    n = "0" + n;
    }

  6. Miguel said

    Lo siento, pero se me escaparon los CIF. Este el codigo bueno

    // Nif español
    tipo = GetTipoDocumento(numero[0]);
    EsIntraComunitario = false;
    if (tipo == TiposCodigosEnum.NIF)
    {
    LetraInicial = string.Empty;
    Int32.TryParse(numero.Substring(0, 8), out n);
    }
    else
    {
    if (tipo == TiposCodigosEnum.CIF)
    {
    LetraInicial = numero.Substring(0, 1);
    Int32.TryParse(numero.Substring(1, 7), out n);
    }
    else
    {
    LetraInicial = numero.Substring(0, 1);

    numero = numero.Replace(“X”, “0”);
    numero = numero.Replace(“Y”, “1”);
    numero = numero.Replace(“Z”, “2”);

    Int32.TryParse(numero.Substring(0, 8), out n);
    }
    }
    DigitoControl = numero.Substring(8, 1);

  7. Miguel said

    La función no es correcta para la verificación de NIE, como bien te indicaban anteriormente si por ej validas Y0870762L actualmente da Falso, cuando es un NIE correcto.

    Si deseas que funcione correctamente para NIE sustituye el código

    Regex regexLetrasNIE = new Regex(“[XYZ]”);
    if ( regexLetrasNIE.IsMatch(letra.ToString()) )
    return TiposCodigosEnum.NIE ;

    Porque actualmente solo hay NIE con X y Y y han previsto que el futuro habrá con Z

    Sustituye también el código:

    // Nif español
    tipo = GetTipoDocumento(numero[0]);
    EsIntraComunitario = false;
    if (tipo == TiposCodigosEnum.NIF)
    {
    LetraInicial = string.Empty;
    Int32.TryParse(numero.Substring(0, 8), out n);
    }
    else
    {
    LetraInicial = numero.Substring(0, 1);

    numero = numero.Replace(“X”, “0”);
    numero = numero.Replace(“Y”, “1”);
    numero = numero.Replace(“Z”, “2”);

    Int32.TryParse(numero.Substring(0, 8), out n);
    }
    DigitoControl = numero.Substring(8, 1);
    }
    Numero = n;

  8. alskare said

    Sí, y por L, por K, por X y por M además de la Y. Aparte de estar ya controlado en la primera versión de la rutina, si miras los comentarios anteriores, verás que ya se había preguntado y respondido casi lo mismo.


    Regex regexLetrasNIE = new Regex("[LKXYM]");
    if ( regexLetrasNIE.IsMatch(letra.ToString()) )
    return TiposCodigosEnum.NIE ;

  9. Miguel said

    Ahora tambien hay NIE que empiezan por Y

  10. Daniel said

    Buenas de nuevo, tengo una duda con respecto al funcionamiento a la hora de validar los NIE, pues me encuentro ante uno que según éste algoritmo es incorrecto, pero según su poseedor es totalmente correcto.

    A continuación pongo un ejemplo con un NIE inventado pero totalmente válido: X09932363C, el problema viene dado por el 0 que encontramos tras la X, aunque éste no suele imprimirse puede venir en algunos NIE, y es entonces cuando el algoritmo falla, pues detecta que el número de caracteres es superior al permitido.

    Aquí os dejo unos cuantos de recursos:
    http://inza.wordpress.com/2008/06/05/nueva-codificacion-del-nie/
    y una web que valida los DNI y los NIE, y en caso de ser NIE obliga a introducir el 0: http://letradni.appspot.com/

    Mi duda es si la Agencia tributaria toma como correctos los NIE sin el 0 a la izquierda o hay que escribirlo.

  11. ElMAR said

    Podrías actualizar el link para descargarse la ultima DLL. la actual tiene fecha 06/09/09 1:20
    Gracias.

  12. alskare said

    Era un problema de actualización de la librería en la página en la que se ejecuta la librería, pero el código sí que lo contemplaba.
    Ya se ha actualizado la librería.

  13. ElMAR said

    Muchas gracias por la librería, pero por si acaso la quieres mejorar un poquito mas, hay un tipo de CIF que no contempla, son por ej los de “Establecimientos permanentes de entidades no residentes en España”. Un ejemplo valido seria W6441002J.
    En la WEB http://club.telepolis.com/jagar1/Economia/Ccif.htm tienes mas informacion

  14. alskare said

    La rutina que tengo puesta no sólo valida los NIE que empiezan por X. También contempla aquellos que empiezan por L, K, Y y M.

  15. Adrian R. said

    Hola Alskare, he estado revisando la librería y me parece genial, solo comentarte que desde el año pasado se empezo a entrgar documentos de identificación NIE con la letra de Inicio “Y” Ejemplo “Y0123456A”, lo que quiere decir, que aparte de la X ahora estas validaciones deben contemplar la letra Y.

    Esta librería los contempla, http://www.alfanet.es/nif.php?codigo=Y0063883A&Submit=Validar

    Un saludo

  16. alskare said

    Yo lo he ido probando en varias aplicaciones y, al final, parece que va reconociendo todo, pero si ves que existe cualquier problema, lo dices, que así lo solventamos como hemos ido haciendo.

  17. Daniel said

    Gracias por este gran aporte, estaba desarrollando mi propia clase, pero he encontrado la tuya Googleando, lo dicho mil gracias.

  18. Cristina said

    Mil gracias! Así da gusto.

    Un S2!

    Cristina

  19. alskare said

    Efectívamente Cristina, hay un error y tal cómo suponía es en un array. A la hora de calcular un “CIF”, en la siguiente línea podemos encontrarnos con que unidades tenga un valor de cero.

    retVal = DigitoControl == letrasCodigo[unidades-1];

    Por ejemplo, con el NIF que me pasaste, la suma de los dígitos pares e impares devuelve un valor de 20, al separar las unidades, nos encontramos con eso, con un 0. Y, por supuesto, al intentar mirar el índice -1 de la matriz letrasCodigo es cuando devuelve esta excepción.

    Bueno, si tenemos en cuenta que, de lo que se trata es de obtener una letra, dependiendo del valor de estas unidades, con la tabla A=1, B=2, etc… vemos que, informáticamente, lo que tenemos que hacer es contar con el 0 (que vemos que no lo resta de 10), así que, en plan rápido se me ocurre contar igual que en la tabla A=1, B=2, C=3 y cambiar el orden de la matriz para que el 0 sea precisamente la letra J. ¿Vaya rollo me ha salido, no?.

    Resumiendo: Sólo se trata de cambiar la letra J para que esté al principio de la matriz y hacer que no reste a la hora de seleccionar el índice de la matriz.

    Por cierto, gracias por el PDF, puesto que tiene unos cuántos NIFs para hacer pruebas.

    Ah.. Por supuesto actualizo el post.

  20. alskare said

    Déjame un día o dos, que me mire el fallo. Tiene toda la pinta de ser un error en algún puntero de algún array. He probado el NIF y me devuelve el error: Index was outside the bounds of the array.

    Gracias por el comunicado.

  21. Cristina said

    Hola, estoy usando tu superfunción, pero tengo un problema. Estoy intentando validar este cif Q0818001J que pertenece a la Universidad de Barcelona http://www.boe.es/boe/dias/2008/07/11/pdfs/A30651-30652.pdf
    Y no lo considera válido ¿Tienes alguna idea?

    Muchas gracias y un saludo!

    Cristina

  22. […] cadenas con Regex ( C#).Escribir en el registro del sistema con C#Exportar datos a formato dbfValidar NIF, NIE, CIFValidar número de cuenta bancaria española « Escribir texto en […]

  23. kiquenet said

    Más info:

    http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.es.csharp&cat=es_ES_fd810b8c-b8f9-4899-9b05-c553ba6738d4&lang=es&cr=ES

  24. alskare said

    Los cambios comentados en los newsgroup sí que están contemplados y, en principio, la rutina ValidaNif está adaptada para la nueva normativa, pero no puedo asegurártelo, puesto que hasta la fecha no he podido probar ningún NIF nuevo.
    Si tuvieras conocimiento de algún nif que haya sido cambiado desde la normativa, te agradecería lo hicieras saber para poder realizar las pruebas y corregir los posibles errores.

  25. kiquenet said

    Está actualizado el código con los comentarios aportados en los newsgroups ??

    Y está incluido los últimos cambios:

    http://www.iberinform.es/Noticias/Actuales/noticia186-cambio-CIF-NIF-identificacion-empresas.htm

    saludos.

  26. alskare said

    No. No tiene ningún tipo de vinculación en ninguna base de datos. De hecho, sí que está pensado para introducir valores en una base; de ahí que el método ToString() devuelva un formato más o menos homogeneizado para poder almacenar los Nif de una manera coherente.

  27. Jose said

    Hola,

    me interesaria saber si el metodo de validacion esta vinculado a alguna base de datos externa?. Estamos valorando la opcion de incorporar un algoritmo similar en nuestra pagina web para validar NIF de empresas en España. Muchas gracias,

    Saludos,

    Jose

Sorry, the comment form is closed at this time.

 
A %d blogueros les gusta esto: