Validador de DNI, NIE e IBAN diseñado con clases de equivalencia
Valida DNI, NIE e IBAN españoles con sus algoritmos de control (letra módulo 23 e IBAN módulo 97) y explica el motivo de cada rechazo. Las pruebas están diseñadas con clases de equivalencia y valores límite: el enunciado incluye la tabla para que veas de dónde sale cada caso.
- Clases de equivalencia
- Valores límite
- Expresiones regulares
- Aritmética modular
- Métodos que devuelven el motivo del error
- switch como expresión
Enunciado
Casi todas las aplicaciones de gestión piden un DNI, un NIE o un IBAN, y casi todas los validan mal: aceptan letras que no corresponden, rechazan minúsculas o se atascan con los espacios del IBAN. Los tres llevan un control matemático que permite detectar la mayoría de los errores al teclear.
La letra del DNI es el resto de dividir el número entre 23, buscado en la tabla TRWAGMYFPDXBNJZSQVHLCKE. El NIE de los extranjeros funciona igual cambiando la X, la Y y la Z iniciales por 0, 1 y 2. El IBAN usa otro método (ISO 13616): se pasan los cuatro primeros caracteres al final, cada letra se sustituye por un número (A = 10, B = 11… Z = 35) y el número resultante, de unas 26 cifras, tiene que dar resto 1 al dividirlo entre 97.
Este ejercicio es también de Entornos de Desarrollo: las pruebas no se han elegido al azar, sino con la técnica de clases de equivalencia (un caso representativo de cada grupo de entradas que el programa trata igual) y valores límite (los bordes de cada grupo). Tienes la tabla abajo: tu programa tiene que pasar todos esos casos.
Qué tiene que hacer el programa
- Cada línea es
TIPO valor, con TIPODNI,NIEoIBANen mayúsculas o minúsculas. Las líneas vacías se ignoran. Para cada línea escribeTIPO valor → válidooTIPO valor → no válido: motivo, con el tipo en mayúsculas y el valor tal como venía (sin espacios al principio ni al final). - DNI: 8 cifras y una letra (mayúscula o minúscula). Si no tiene ese formato,
formato incorrecto (8 cifras y una letra); si la letra no es la que toca,la letra debería ser L. - NIE: X, Y o Z, 7 cifras y una letra. Si no tiene ese formato,
formato incorrecto (X, Y o Z, 7 cifras y una letra); si la letra no cuadra, el mismo mensaje que el DNI. - IBAN (se ignoran los espacios y las minúsculas): si no empieza por ES,
solo se admiten IBAN españoles; si no tiene 24 caracteres,un IBAN español tiene 24 caracteres; si tras ES hay algo que no sea una cifra,después de ES solo puede haber cifras; si el control módulo 97 no da 1,los dígitos de control no cuadran. Se comprueba en ese orden. - Un tipo desconocido escribe
TIPO valor → no válido: tipo de documento desconocido. - Al final,
Válidos: V de T.
Entrada
Una línea por documento: DNI valor, NIE valor o IBAN valor.
Datos de referencia
| Clase | Caso de prueba | Resultado esperado |
|---|---|---|
| Válida: 8 cifras y su letra | 12345678Z | válido |
| Válida: letra en minúscula | 12345678z | válido |
| Límite inferior del número | 00000000T | válido |
| Límite superior del número | 99999999R | válido |
| No válida: letra que no toca | 12345678A | la letra debería ser Z |
| No válida: 7 cifras | 1234567Z | formato incorrecto |
| No válida: 9 cifras | 123456789Z | formato incorrecto |
| No válida: sin letra | 12345678 | formato incorrecto |
| Clase | Caso de prueba | Resultado esperado |
|---|---|---|
| Válida, con espacios | ES91 2100 0418 4502 0005 1332 | válido |
| Válida, en minúsculas y sin espacios | es9121000418450200051332 | válido |
| Otro país | DE89370400440532013000 | solo se admiten IBAN españoles |
| Longitud incorrecta | ES912100041845020005133 | 24 caracteres |
| Letras tras ES | ES91 2100 0418 4502 0005 13AB | solo cifras |
| Control incorrecto (una cifra cambiada) | ES91 2100 0418 4502 0005 1333 | no cuadran |
Ejemplos de ejecución
Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.
DNI y NIE
Entrada
DNI 12345678Z DNI 12345678z DNI 12345678A DNI 1234567Z NIE X1234567L NIE W1234567L PASAPORTE AB123456
Salida por consola
DNI 12345678Z → válido DNI 12345678z → válido DNI 12345678A → no válido: la letra debería ser Z DNI 1234567Z → no válido: formato incorrecto (8 cifras y una letra) NIE X1234567L → válido NIE W1234567L → no válido: formato incorrecto (X, Y o Z, 7 cifras y una letra) PASAPORTE AB123456 → no válido: tipo de documento desconocido Válidos: 3 de 7
IBAN
Entrada
IBAN ES91 2100 0418 4502 0005 1332 iban es9121000418450200051332 IBAN DE89370400440532013000 IBAN ES912100041845020005133 IBAN ES91 2100 0418 4502 0005 13AB IBAN ES91 2100 0418 4502 0005 1333
Salida por consola
IBAN ES91 2100 0418 4502 0005 1332 → válido IBAN es9121000418450200051332 → válido IBAN DE89370400440532013000 → no válido: solo se admiten IBAN españoles IBAN ES912100041845020005133 → no válido: un IBAN español tiene 24 caracteres IBAN ES91 2100 0418 4502 0005 13AB → no válido: después de ES solo puede haber cifras IBAN ES91 2100 0418 4502 0005 1333 → no válido: los dígitos de control no cuadran Válidos: 2 de 6
Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. Un método por documento que explique el error
Haz que cada validador devuelva null si el documento es correcto o un String con el motivo. El main solo tiene que escribir lo que le digan, y cada validador se puede probar por separado (es lo que harías con JUnit).
2. La letra del DNI
Primero el formato con matches("\\d{8}[A-Z]") (tras pasar a mayúsculas). Después, la letra esperada es LETRAS.charAt(numero % 23).
char esperada = LETRAS.charAt(Integer.parseInt(dni.substring(0, 8)) % 23);3. El NIE reutiliza el DNI
Comprueba su formato y sustituye la primera letra por su posición en "XYZ": el resultado es un DNI de 8 cifras con su letra, y puedes llamar a validarDni sin repetir código.
4. El módulo 97 sin números gigantes
El número del IBAN reordenado tiene 26 cifras: no cabe en un long. Pero el resto de una división se puede calcular cifra a cifra, como se hace a mano: resto = (resto * 10 + cifra) % 97. Las letras se cambian por su número de dos cifras antes.
for (char cifra : valor.toCharArray())
resto = (resto * 10 + (cifra - '0')) % 97;5. Prueba con la tabla
Antes de ejecutar las pruebas del editor, recorre la tabla de clases de equivalencia y comprueba que tu programa da el resultado esperado en cada fila. Si añades una regla nueva, añade también su clase.
Resuélvelo aquí
El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 2 casos ocultos que buscan los errores típicos.
Ejemplo
DNI 12345678Z DNI 12345678z DNI 12345678A DNI 1234567Z NIE X1234567L NIE W1234567L PASAPORTE AB123456
DNI 12345678Z → válido DNI 12345678z → válido DNI 12345678A → no válido: la letra debería ser Z DNI 1234567Z → no válido: formato incorrecto (8 cifras y una letra) NIE X1234567L → válido NIE W1234567L → no válido: formato incorrecto (X, Y o Z, 7 cifras y una letra) PASAPORTE AB123456 → no válido: tipo de documento desconocido Válidos: 3 de 7
Solución explicada
Ver la solución completa
1import java.util.Scanner;
2
3public class Main {
4 static final String LETRAS = "TRWAGMYFPDXBNJZSQVHLCKE";
5
6 /** null si el DNI es correcto, o el motivo por el que no lo es. */
7 static String validarDni(String dni) {
8 dni = dni.toUpperCase();
9 if (!dni.matches("\\d{8}[A-Z]")) return "formato incorrecto (8 cifras y una letra)";
10 char esperada = LETRAS.charAt(Integer.parseInt(dni.substring(0, 8)) % 23);
11 return dni.charAt(8) == esperada ? null : "la letra debería ser " + esperada;
12 }
13
14 /** El NIE se valida como un DNI cambiando X, Y y Z por 0, 1 y 2. */
15 static String validarNie(String nie) {
16 nie = nie.toUpperCase();
17 if (!nie.matches("[XYZ]\\d{7}[A-Z]")) return "formato incorrecto (X, Y o Z, 7 cifras y una letra)";
18 return validarDni("XYZ".indexOf(nie.charAt(0)) + nie.substring(1));
19 }
20
21 /** IBAN español: 24 caracteres y control módulo 97 (ISO 13616). */
22 static String validarIban(String iban) {
23 iban = iban.replace(" ", "").toUpperCase();
24 if (!iban.startsWith("ES")) return "solo se admiten IBAN españoles";
25 if (iban.length() != 24) return "un IBAN español tiene 24 caracteres";
26 if (!iban.substring(2).matches("\\d{22}")) return "después de ES solo puede haber cifras";
27 // Se pasan los 4 primeros caracteres al final, las letras a números (A = 10 … Z = 35)
28 // y se calcula el resto entre 97 cifra a cifra, sin construir un número enorme
29 String reordenado = iban.substring(4) + iban.substring(0, 4);
30 int resto = 0;
31 for (char c : reordenado.toCharArray()) {
32 String valor = Character.isLetter(c) ? String.valueOf(c - 'A' + 10) : String.valueOf(c);
33 for (char cifra : valor.toCharArray()) resto = (resto * 10 + (cifra - '0')) % 97;
34 }
35 return resto == 1 ? null : "los dígitos de control no cuadran";
36 }
37
38 public static void main(String[] args) {
39 Scanner sc = new Scanner(System.in);
40 int total = 0, validos = 0;
41 while (sc.hasNextLine()) {
42 String linea = sc.nextLine().trim();
43 if (linea.isEmpty()) continue;
44 int espacio = linea.indexOf(' ');
45 String tipo = (espacio < 0 ? linea : linea.substring(0, espacio)).toUpperCase();
46 String valor = espacio < 0 ? "" : linea.substring(espacio + 1).trim();
47 String error = switch (tipo) {
48 case "DNI" -> validarDni(valor);
49 case "NIE" -> validarNie(valor);
50 case "IBAN" -> validarIban(valor);
51 default -> "tipo de documento desconocido";
52 };
53 total++;
54 if (error == null) validos++;
55 System.out.println(tipo + (valor.isEmpty() ? "" : " " + valor) + " → " + (error == null ? "válido" : "no válido: " + error));
56 }
57 System.out.println("Válidos: " + validos + " de " + total);
58 }
59}Cada validador devuelve el motivo del error en lugar de un simple boolean. Eso hace el programa más útil para el usuario y las pruebas más precisas: no basta con saber que algo es inválido, hay que saber que lo es por la razón correcta.
El NIE se reduce a un DNI y se valida con el mismo método: reutilizar en lugar de copiar significa que un error corregido en el DNI queda corregido también en el NIE.
El cálculo del módulo 97 cifra a cifra se basa en que (a × 10 + b) mod 97 = ((a mod 97) × 10 + b) mod 97: nunca hay un número mayor que 970. Es lo que permite comprobar IBAN en cualquier lenguaje sin BigInteger.
Las pruebas salen de la tabla de clases de equivalencia y valores límite: un caso por cada grupo que el programa trata igual y los extremos (00000000 y 99999999). Con ocho casos bien elegidos se cubre lo que no cubrirían cien números al azar.
Para ir más allá
- Valida también el CIF de las empresas, que tiene un dígito o letra de control calculado de otra forma.
- Escribe las pruebas de los tres validadores con JUnit 5 y
@ParameterizedTesta partir de las tablas. - Calcula los dígitos de control de un IBAN a partir del código de cuenta (CCC) de 20 cifras.