Introducción

En el capítulo anterior explicamos un proceso para validar formularios. La idea general del proceso se basa en crear la estructura de los campos del formulario en un array, tarea que podemos denominar como definición de campos. Esta estructura nos permitirá automatizar la tarea de construir dinámicamente el formulario con PHP, almacenar los valores recibidos y finalmente nos servirá para realizar la validación de los campos.

He desarrollado un segundo ejemplo form2.php incorporando un mayor número de campos de varios tipos para exponer detalles de cómo fueron definidos y explicar lo relacionado con las expresiones regulares para verificar los tipos. El código del script form2.php es igual que el del ejemplo del capítulo anterior form1.php, donde sólo cambia la definición de campos ($array_campos) que ahora es más extensa. Puede ver los codigos completos.

Definiciones de campos de texto

Conviene recordar los principales tipos de campos HTML para el envío de datos de formulario:

Campo deTagAtributos
Textoinputtype="text"
Texto ocultoinputtype="hidden"
Texto contraseñainputtype="password"
Verificacióninputtype="checkbox"
Área de textotextareacols="n"
rows="m"
Selector (botones de radio)inputtype="radio"
Menú de opcionesselect, option

No debemos olvidar que un navegador no remitirá un campo si este no tiene valor. Por ejemplo, si un formulario tiene un campo para introducir un número entero, podríamos tener algo como esto <input type="text" name="numero" value="" />. El navegador no remitirá éste campo debido a que el valor está vacío. Por lo tanto en el servidor sólo podríamos validar los campos recibidos, sin saber nada acerca de los campos que no se enviaron por haber sido dejado vacíos por el usuario.

Como hemos visto, los campos de texto son los <input> con type igual a text, hidden o password. Para crear una definición de campo de texto como <input type="text" name="numero" value="" /> insertaremos en el $array_campos la siguiente entrada:

"numero" => array(
    "valor" => "0",
    "titulo" => "Un número",
    "tipo" => "número entero", 
    "longitud-min" => 1,
    "longitud-max" => 16,    
    "requerido" => true,
    "tag" => "input",
    "atributos" => array(
        "type" => "text",
        "class" => PREFIJOVAL
        )
    )
    

Explicamos las claves de esta entrada:

  1. La clave general del array, en este caso "numero", es también el nombre del campo en el formulario. Como no debe haber campos con nombres repetidos, tampoco habrán más de una clave con el mismo nombre.
  2. "valor": Será siempre una cadena y aquí ponemos el valor inicial que tendrá el campo al ser construido dinámicamente por el script construir_form(). Como esperamos un número entero, este campo ha de tener inicialmente y siempre algún número entero. Cuando recibamos los datos por POST, almacenaremos el valor recibido en este entrada, a la vez que de aquí lo tomaremos para validarlo.
  3. "titulo": Se trata del título que aparecerá como etiqueta previa al campo cuando construyamos el formulario.
  4. "tipo: Es una clave que debe corresponderse con alguna del array de patrones de expresiones regulares, si es que queremos que se verifique el tipo de dato con algún patrón. Si no quisiéramos verificar el tipo, podemos poner la palabra "valor" con lo que nos saltaremos la verificación de tipos, aunque no dejará de validarse si es requerido así como la longitud del campo. Además del tipo "valor" hay dos tipos que no se verifican con los patrones de expresiones regulares: el tipo "selector" y el tipo "menú". Se usan para validar elementos <input type="radio"> y <select>. En estos la validación consiste en comprobar que la opción recibida está dentro de la lista de opciones requeridas.
  5. "longitud-max", "longitud-min": Todos los valores envíados por un formulario se hacen como texto, por lo que debemos declarar una longitud máxima y mínima que serán validadas. Al construir el campo que tengan atributos relacionados con la longitud máxima, como son size y maxlength para los elementos <input>, esta entrada creará esos atributos.
  6. "requerido": Si un campo es requerido, debe venir con la colección de campos recibidos en el POST. Si no es requerido será opcional, pero si no viene con el POST entonces le damos el valor que porte en este array. Pero en todo caso, tanto si es requerido (y debe recibirse) como si es opcional y se reciba, validaremos su longitud así como verificaremos el tipo (si no tiene puesto un tipo "valor").
  7. "tag": Se trata de la etiqueta HTML input que nos permitirá construir ese elemento.
  8. "atributos": Son los atributos HTML del elemento. Para el ejemplo necesitamos el atributo type = "text" para crear un elemento de cuadro de texto. Pero además incorporamos el atributo class con el valor dado por la constante PREFIJOVAL que ya hemos explicado anteriormente. Si un campo tiene una clase que empiece por esta constante será validado en todos sus extremos: requerimiento, longitud y tipos. En otro caso se obviará la validación. Podríamos incluir otros atributos como style o incluso eventos, en definitiva, cualquier atributo que soporte ese elemento.

Una vez que el script valida-form.php mediante su función construir_form() construya el formulario, tendremos el siguiente HTML para ese campo de ejemplo:

<label><span>Un número<sup style="color:red">*</sup>: </span>
<input name="numero" title="número entero#requerido#1#16" 
type="text" class="val-" size="16" 
maxlength="16" value="0" /></label><br />

El atributo title se construye usando las entradas tipo, requerido, longitud mínima y máxima. Se separan con el caracter # para que JavaScript pueda disponer de estas configuraciones cuando proceda a validar el formulario. Veáse que en este caso la longitud máxima queda determinada aquí, pero también en los atributos size y maxlength. Aunque la validación sólo hará uso de la longitud expresada en el atributo title. Ese título podrá ser observado por el usuario, dándole información acerca del campo que está rellenando.

Resumimos algunas consideraciones a tener en cuenta a la hora de construir una definición de campo en relación con el proceso de validación:

  • Con class != PREFIJOVAL se acepta el valor sin realizar ninguna validación. En otro caso se validará.
  • Será un error si el tipo no es "valor", "selector", "menú" o no está en la lista de tipos de los patrones de expresiones regulares.
  • Si es un tipo aceptado, se verifican las longitudes.
  • Si es requerido se verifica que el campo no esté vacío.
  • Si el tipo es "valor" no se realizan más validaciones.
  • Si en cambio el tipo es "selector" o "menú" se verifica que el valor recibido está en la lista de opciones.
  • Si en cambio el tipo es alguno de la lista de patrones, se verifica.

Sobre la validación de la longitud del campo puede surgir una duda. Si tenemos un patrón de expresión regular como /^\w{32}$/ significa que aceptará cadenas con una longitud máxima de 32 caracteres [A-Za-z0-9_]. E incluso podemos especificar un rango de longitudes mínima y máxima como por ejemplo {5,10} significando que la cadena debe contener entre 5 y 10 caracteres. Si usáramos expresiones regulares teniendo en cuenta esto nos ahorraríamos declarar las longitudes máxima y mínima en el array de definiciones del campo.

Pero si establecemos que los patrones declarados han de considerarse genéricos para cualquier longitud de campo de un mismo tipo, es preferible que estos no impongan restricciones en las longitudes sino en los tipos. Veámos un ejemplo, el patrón /^(?:0|[1-9]\\d*)$/ valida números naturales pero sin longitud máxima especificada. La mínima si queda cubierta, pues o bien es un 0,1,2,...,9 o bien es algún número que empiece por 1,2,...,9 con una cantidad indefinida de dígitos. Podíamos especificar longitudes máximas por ejemplo haciendo /^(?:0|[1-9]\\d{15})$/ de tal forma que sería un número natural de 1 a 16 dígitos. Pero si luego necesitamos un campo para un número natural de una cantidad de dígitos distintos, nos obligará a modificar el patrón.

Y eso es lo que tenemos que evitar a toda costa, modificar los patrones. Estos deben ser tan genéricos que cubran todas las longitudes que podamos necesitar. Luego en las definiciones podemos limitar esas longitudes, como en este ejemplo:

DefiniciónPatrón de expresión regular
"numero-natural" => array(
    "valor" => "0",
    "titulo" => "Número natural",
    "tipo" => "número natural",
    "longitud-min" => 1,
    "longitud-max" => 16,
    "requerido" => true,
    "tag" => "input",
    "atributos" => array(
        "type" => "text",
        "class" => PREFIJOVAL
        )
    )
"número natural" => array(
    "patron" => "^(?:0|[1-9]\\d*)$",
    "mensaje" => "Debe ser un número natural (no negativo).")

Nuestro proceso verificará que el valor tiene entre 1 y 16 caracteres según se especifica en la definición. Si esto se verifica se pasará el test del patrón a continuación, comprobándose que los dígitos se disponen tal como muestra el patrón.

Otras definiciones de campos

Campo área de texto

Un campo área de texto es el elemento <textarea>. La definición para este campo es igual que para los campos de texto que vimos en el apartado anterior. Por ejemplo, este campo es uno de los del ejemplo:

"mensaje-2" => array(
    "valor" => "",
    "titulo" => "Mensaje con texto general",
    "tipo" => "texto Latín-B", 
    "longitud-min" => 1,
    "longitud-max" => 150,
    "requerido" => true,
    "tag" => "textarea",
    "atributos" => array(
        "cols" => "45",
        "rows" => "5",
        "class" => PREFIJOVAL,
        "onkeypress" => "javascript:controlaTextarea(this, event, 150);"
        )
    ),

HTML requiere para un <textarea> los atributos para especificar columnas y filas (cols y rows). Se incluye un evento onkeypress para controlar la longitud del campo a medida que el usuario va tecleando caracteres, usando la función controlaTextarea() que se encuentra en el script vinculado en la cabecera del documento (general.js). Como el campo es requerido, imponemos longitud mínima de 1 caracter y máxima de 150. Si fuera opcional pondríamos como longitud mínima un 0, pudiendo limitar la máxima para evitar textos excesivamente largos.

Campo casilla de verificación

Un campo de verificación (o casilla de verificación) es un elemento <input type="checkbox">. En el ejemplo encontramos un par de ellas con las siguientes definiciones:

"ver-email" => array(
    "valor" => "si",
    "titulo" => "Publicar e-mail",
    "tipo" => "verificación", 
    "longitud-min" => 2,
    "longitud-max" => 2,    
    "requerido" => true,
    "tag" => "input",
    "atributos" => array(
        "type" => "checkbox",
        "class" => PREFIJOVAL,
        "checked" => ""
        )
    ),    
"enviar-email" => array(
    "valor" => "si",
    "titulo" => "Enviar e-mail",
    "tipo" => "verificación", 
    "longitud-min" => 2,
    "longitud-max" => 2,  
    "requerido" => true,
    "tag" => "input",
    "atributos" => array(
        "type" => "checkbox",
        "class" => PREFIJOVAL,
        "checked" => "checked"
        )
    ),

Las verificaciones se caracterizan por ser remitidas por el navegador si están pulsadas, lo que se advierte si tienen el atributo "checked" = "checked". En otro caso si no están pulsados, el elemento no poseerá este atributo y no se enviará nada al servidor. Si se envía, se remite el atributo "value". Aquí estamos dando el valor "si" para que se remita cuando la casilla sea pulsada, en cuyo caso modificamos "checked" = "checked", pero si esa casilla no se recibe entonces ponemos "checked" = "". En definitiva, lo que nos dice si una casilla está pulsada en el array es la entrada "checked" y no "valor". Puede parecer un poco líoso, pero es la forma que tiene un formulario de enviar los datos de un checkbox.

Campo selector de botones de radio

Los campos selectores o radios son los elementos <input type="radio">. En el ejemplo tenemos el siguiente:

"sistema-operativo" => array(
    "valor" => "Sin especificar",
    "titulo" => "Sistema Operativo",
    "opciones" => array(
        "Sin especificar" => "Sin especificar",
        "Windows" => "Windows",
        "Mac OS X" => "Mac OS X",
        "GNU/Linux" => "GNU/Linux",
        "Linux" => "Linux",
        "Otros" => "Otros"        
        ),
    "tipo" => "selector",
    "estilo-fieldset" => "width: 20em",
    "clase-fieldset" => "",
    "separador-selector" => " ",
    "longitud-min" => 1,    
    "longitud-max" => 20,
    "requerido" => true,
    "tag" => "input",
    "atributos" => array(
        "type" => "radio",
        "class" => PREFIJOVAL
        )
    )

La validación de este campo consiste en comprobar que el valor recibido es alguno de la lista de opciones. El tipo debe ser necesariamente "selector" para que el proceso construya y valide el elemento. Se incluyen algunas nuevas entradas de definición particulares para este campo:

  • opciones: Un array de las opciones o botones de radio que se crearán todos con el mismo nombre. Constituye una matriz de controles cuyo valor es el del botón que esté pulsado. Inicialmente se construye con el primer botón pulsado, usualmente alguno con el valor "Sin especificar". El array de opciones son parejas "título" => "valor" para declarar el título y el valor de cada botón de radio, quedando construido el elemento de esta forma:
    <fieldset style="width: 20em">
    <legend>Sistema Operativo: </legend>
        <label><span>Sin especificar:</span>
        <input name="sistema-operativo" title="selector#requerido#1#20" 
        type="radio" class="val-" checked="checked" value="Sin especificar" />
        </label> 
        ...similar para el resto de opciones,
           aunque sin atributo checked...
    </fieldset>
    Aunque en este ejemplo título y valor son iguales, no es necesario que lo sean.
  • estilo-fieldset: El grupo de botones se inserta en un elemento <fieldset>, declarando un estilo si lo deseamos en esta entrada (se aplica al atributo style).
  • clase-fieldset: Adicionalmente podemos adjudicarles un nombre de clase de estilo (atributo class).
  • separador-selector: Los botones de radio pueden separarse entre sí con esta cadena para insertar HTML. En el código anterior vemos el espacio resaltado en verde que separará un botón de radio de otro. Puede usarse cualquier literal HTML, como un salto de línea <br />.
Noviembre 2013

El ejemplo de este tema form2.php y el del tema anterior form1.php usaban originalmente los scripts valida-form.php y /res/inc/valida-form.js. Esos originales se usaban también en este sitio para las páginas de contacto.php y el buscador.php, pero han sido objeto de varias modificaciones. He preferido seguir manteniendo las primeras versiones para los ejemplos pero las he renombrado como valida-form-obs.php y /res/inc/obs/valida-form.js. El PHP original se sigue llamando valida-form.php mientras que el JS original luego se renombró como validador-form.js.

En esos originales ya no uso las definiciones estilo-fieldset y clase-fieldset para no complicar más este conjunto de definiciones. La estructura generada del campo en los script originales de este sitio ahora es como sigue:

<label id="cont-NOMBRE">
    <span id="etiq-NOMBRE">
        TÍTULO
        <span class="asterisco">*</span>
    </span>
    <iput name="NOMBRE" ... />
<label>    
    

Vemos que cada campo se rodea de un <label> y luego hay un <span> para exponer el título del campo según la definición "titulo". He agregado una nueva definición tag-label que por defecto si no se define tendrá la cadena "label". Cuando sea un tipo "selector" podemos definir "tag-label" => "fieldset" y el script usará un <legend> en lugar de un <span> para incorporar el título. Podemos dar a ese tag-label sea cual sea el tag elegido las siguientes definiciones:

  • "clase-label" => "miClase"
  • "estilo-label" => "width: 20em;"

Además ahora también le pongo un ID al contenedor y al título con objeto de poder manipularlos individualmente en la página, por ejemplos para ocultarlos o darles cualquier estilo. El asterisco también está identificado con una clase para darles estilo.

Campo menú de opciones

El campo menú es el elemento <select> con elementos <option> como entradas de menú. En el ejemplo tenemos esta definición:

"navegador" => array(
    "valor" => "Sin especificar",
    "titulo" => "Navegador",
    "opciones" => array(
        "Sin especificar" => "Sin especificar",    
        "Internet Explorer" => "Internet Explorer",
        "Firefox" => "Firefox",
        "Opera" => "Opera",
        "Safari" => "Safari",
        "Google Chrome" => "Chrome",
        "Otros" => "Otros"
        ),
    "tipo" => "menú", 
    "longitud-min" => 1,    
    "longitud-max" => 20,
    "requerido" => true,
    "tag" => "option",
    "atributos" => array(
        "class" => PREFIJOVAL,
        "size" => 1
        )
    ),    
    

El tipo es obligatoriamente menú para que el proceso pueda construir y validar este campo. El tag será option. Se observa que tiene una entrada de opciones como el tipo de botones de radio anterior. Son parejas "título" => "valor" que se insertan en los <option value="valor">título</option>. Este ejemplo queda finalmente en HTML como sigue:

<label><span>Navegador: </span>
<select name="navegador" title="menú#requerido#20" 
class="val-" size="1">
    <option value="Sin especificar" selected="selected">
        Sin especificar</option>
    <option value="Internet Explorer">
        Internet Explorer</option>
    ...
</select>
</label>

Se observan los títulos resaltados en amarillo y los valores en azul. Como para los botones de radio, no tienen porqué ser necesariamente iguales. También se inicia con el primero de ellos seleccionado (atributo selected), por lo que puede usarse una opción similar a "Sin especificar".

Validando con expresiones regulares

Los valores recibidos del formulario que se validarán estarán en la entrada "valor" de un array, como la variable $array_campos que vimos en los ejemplos de form1.php y form2.php. O bien los valores se encuentran directamente en cada campo del formulario cuando validemos con Javascript. La verificación de tipos se realiza con expresiones regulares. Se dispone de un array llamado $patrones_tipos_base que se ubica en el script valida-form.php. Un par de entradas de este array para el ejemplo expuesto son las siguientes:

$patrones_tipos_base = array(
IDENTIFICAFORM => array(
    "patron" => "^[a-zA-Z0-9]+$",
    "mensaje" => "Identificador de formulario no válido."),
"número natural" => array(
    "patron" => "^(?:0|[1-9]\\d*)$",
    "mensaje" => "Debe ser un número natural (no negativo)."),      
    ...

Cada patrón se identifica por una clave que es el tipo relacionado en el array de definiciones $array_campos. Aquí vemos el primer tipo para el campo identificador de formulario. El patrón es una expresión regular que verificará que el identificador sea una cadena de texto con letras a-z, A-Z o dígitos 0-9 y con una longitud de 1 o más caracteres. La longitud mínima o máxima se especifica en la definición del campo, usualmente de 32 dígitos. Por otro lado el mensaje permitirá mostrar un informe de error al usuario si no se cumple el patrón. Observe que los patrones se declaran como cadenas de texto, por lo que hay que escapar los caracteres afectados a los tipo String, como es la barra invertida y las comillas dobles si se necesitaran. La función valida_form() del script valida-form.php se encarga de aplicar estos patrones:

...   
$patron = "/".$patrones_tipos[$arr["tipo"]]["patron"]."/";
if (!preg_match($patron, $arr["valor"])){
    $mensaje .= "<li>Error en el campo '".$arr["titulo"]."'".
    ": ".$patrones_tipos[$arr["tipo"]]["mensaje"].
    " ".$mensaje_longitud."</li>";
}
...

Vemos que se le añaden los caracteres de inicio y final de expresión regular, las barras derecha "/", por lo que si se necesitan en los patrones del array $patrones_tipos_base hay que escaparlas con la barra invertida: "\/". La función preg_match() devolverá verdadero si encuentra una coincidencia o falso en otro caso.

En el script PHP que emite el formulario, después de insertar el array de definiciones de campos, llamamos a la función contruir_array_patrones(). Tiene el siguiente código:

function contruir_array_patrones($array_campos){
    global $patrones_tipos_base;
    global $patrones_tipos;
    $arr_tipos = array();
    foreach ($array_campos as $arr){
        $tipo = $arr["tipo"];
        if (($tipo != "valor")&&($tipo != "selector")&&
            ($tipo != "menú")&&
            (!in_array($arr["tipo"], $arr_tipos))){
            $arr_tipos[] = $arr["tipo"];
        }
    }
    foreach ($arr_tipos as $tipo){
        $patron = $patrones_tipos_base[$tipo]["patron"];
        $mensaje = $patrones_tipos_base[$tipo]["mensaje"];
        $patrones_tipos[$tipo] = array("patron"=>$patron, "mensaje"=>$mensaje);
    }
}

Se trata de extraer sólo los patrones del $array_patrones_base que se vayan a usar en este formulario (los declarados en el array de definiciones $array_campos), y ponerlos en un nuevo array $array_patrones. Así no estamos manejando un array con patrones que no se van a usar y, por otro lado, exportaremos sólo los patrones necesarios a Javascript. Puesto que cuando PHP construye el HTML con el formulario para enviarlo al usuario, inserta un elemento <script> en la cabecera del documento usando la función construir_array_js() del script valida-form.php:

function construir_array_js(){
    global $patrones_tipos;
    $cadena = "";
    foreach ($patrones_tipos as $tipo => $arr){
        if ($cadena != "") $cadena .= ",";
        $cadena .= "\"".$tipo."\",/".$arr["patron"]."/,\"".$arr["mensaje"]."\"";
    }
    $cadena = "<script type=\"text/javascript\">\r\n".
                  "//<![CDATA[\r\n".
                    "var patronesTipos = Array(".$cadena.");\r\n".
                  "//]]>\r\n".
              "</script>\r\n";
    return $cadena;
}

Así el navegador dispone de la misma lista de patrones que el servidor para realizar la validación. En el <head> de la página podemos ver entonces la declaración del array patronesTipos para usar con Javascript:

<script>
var patronesTipos = Array(
    "identifica-form2",
        /^[a-zA-Z0-9]+$/,
        "Identificador de formulario no válido.",
    "número natural",
        /^(?:0|[1-9]\\d*)$/,
        "Debe ser un número natural (no negativo).",
    ...
    );
</script>

El array tiene un sólo nivel apareciendo los tipos, patrones y mensajes todos seguidos. Luego los rescatamos de esta forma en la función validaCampo() que está en valida-form.js:

function validaCampo(campo, valor, titulo, tipo, requerido, 
                     longitudMin, longitudMax){
    ...               
    for (var i=0; i<patronesTipos.length; i=i+3){
        if (tipo == patronesTipos[i]){
            var validado = patronesTipos[i+1].test(valor);
            if (!validado) masMensaje = patronesTipos[i+2];
            encontrado = true;
            break;
        }                
    }
    ...

Vamos iterando por el array patronesTipos en grupos de 3 índices hasta encontrar el tipo. Luego aplicamos la expresión regular con la funcion test de Javascript que devuelve verdadero si se encuentra coincidencia con el patrón o falso en otro caso.

Por lo tanto la declaración de patrones sólo la hacemos en PHP, pudiendo ser además el archivo validar-form.php un recurso genérico para todo el sitio web. El traslado de esta lista de patrones hasta el navegador nos garantiza el principio de doble validación, generando en su caso los mismos mensajes de error en servidor y navegador. Esto es posible debido a que las expresiones regulares son bastante estándar, al menos hasta cierto punto. En todo caso si se necesitara que una expresión regular tuviese una distinta estructura en PHP y Javascript, podríamos modificar el array de patrones para que albergara esa posibilidad (quizás desglosar la entrada patrón en dos, una para PHP y otra para Javascript). Sin embargo y al menos inicialmente, pienso que es mejor crear expresiones regulares que funcionen en ambos lenguajes, tal como las que he usado en el ejemplo form2.php y que se detallan a continuación

Ejemplos de patrones de expresiones regulares

Las expresiones regulares son un recurso muy potente, pero requiere un esfuerzo de aprendizaje. El hecho de que se usen en todos los lenguajes de una forma bastante estándar supone un conocimiento adquirido que siempre nos será muy útil. Podemos limitarnos a buscar patrones para validar campos y copiar y pegar sin más. Pero saber como funcionan nos garantizará conocer una técnica que puede sacarnos de muchos apuros en otros problemas de programación.

En este apartado no voy a meterme de lleno con esta materia, pues requeriría más espacio y tampoco es el objeto de esta serie de capítulos. Sólo explicaré los patrones que he usado en el ejemplo, entendiendo que quizás no son los más adecuados o probablemente hayan otros más eficientes para el mismo cometido. He usado patrones muy simples, pues con expresiones regulares se pueden hacer muchas más cosas. En todo caso intentaré dejar una primera impresión de acercamiento a las expresiones regulares para todos aquellos que aún no se hayan atrevido con ellas.

Recuerde que no verificamos longitudes máximas ni mínimas para todo el patrón, pues esta tarea la ejecutábamos con las declaradas en la definición del campo. Volvemos a repetir que este enfoque nos permite disponer de un juego de patrones genéricos para usar con campos de diferentes longitudes. Por otro lado nuestras expresiones regulares deben buscar coincidencia en todo el valor del campo, por lo que se inician con ^ y finalizan con $. Con estas anclas expresamos que el valor debe coincidir totalmente con el patrón.

Identificador de formulario

^[a-zA-Z0-9]+$

Recordemos que este identificador es una cadena de letras y números tal como sale del hash o signatura del MD5. Tiene 32 bytes aunque aquí sólo verificamos que tenga 1 o más caracteres, pues las longitudes se verifican con la definición del campo. Una clase de caracteres se encierra entre corchetes. Un rango se expresa con el guión. Por ejemplo, con [x85h] buscaríamos coincidencia con algún caracter de la lista: "x", "8", "5" y "h". Con [h-x5-8] buscaríamos coincidencias con la lista: "h,i,j,...,x" y con la lista "5,6,7,8". Por lo tanto nuestro patrón buscará coincidencias con las listas "a,b,..,z", "A,B,...,Z" y con "0,1,2,...9". El carácter + es un cuantificador y nos dice que deben aparecer 1 o más coincidencias de los caracteres de las listas anteriores.

Número natural

^(?:0|[1-9]\d*)$

Los paréntesis son una subexpresión. Cuando se inician con ?: podemos usarla para expresar distintas alternativas de coincidencia haciendo uso del separador | (barra vertical). Así en este caso un número natural será...

  • 0: un cero que aparecerá una sola vez, o bien...
  • [1-9]\d*: algún número que empiece con un dígito entre 1 y 9 seguido de cero o más dígitos 0 a 9.

Observe que \d es igual que poner [0-9], mientras que el cuantificador * significa que se permite cero o más coincidencias. Este patron no permitirá valores como "1a23" o "023" por ejemplo.

Número entero

^(?:0|\-?[1-9]\d*)$

Un número entero puede ser cero, un natural positivo o negativo. En este caso volvemos a usar la subexpresión con alternativas. Un número entero es un cero, o un guión de negativo (se ha de escapar con "\") que podrá aparecer cero o una vez (cuantificador ?), seguido de un número entre 1 y 9, seguido de cualquier cantidad de dígitos (cuantificador *).

Número real

^(?:0|\-?[1-9]\d*)(?:\.\d+)?$

Para confeccionar el patron del número real podemos estructurarlo en la parte entera, el punto decimal y la parte decimal. Pero podemos incluso pensar que un número real es un número entero, un punto decimal y una serie de dígitos que forman la parte decimal. Aprovechando el patrón del número entero, lo aplicamos a la primera subexpresión. Luego incluimos otra subexpresión (?:\.\d+})? que en este caso sólo tiene una alternativa en su interior. Dice que la coincidencia se produce si encontramos un punto (debe ser escapado) seguido de 1 o más dígitos. Observe que al final hay un ?, se trata en este caso del cuantificador que quiere decir que esta coincidencia debe aparecer 0 o 1 vez. En definitiva, que podemos validar algo como "123.005" pero no "123." por ejemplo. Veáse que la longitud queda sin especificar, pues la validamos con la definición del campo.

Nombres de usuario

^\w+$

La \w (ha de escaparse) representa la clase [A-Za-z0-9_], compuestas por letras ASCII, dígitos y el guión bajo. Así un nombre de usuario para un hipotético "login" podríamos limitarlo a estos caracteres, usando además el cuantificador + que nos dice que debe tener uno o más caracteres.

Contraseñas

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\w+$

A veces necesitamos imponer unas condiciones para las contraseñas, por ejemplo:

  • Que tengan entre 4 y 10 caracteres
  • Que tengan al menos una letra minúscula
  • Que tengan al menos una letra mayúscula
  • Que tengan al menos un dígito

Antes vimos las subexpresiones con (?: ) donde podíamos usar alternativas o simplemente para agrupar una expresión y usarla para luego cuantificarla, por ejemplo. Cuando una expresión tiene varias subexpresiones (como la del caso del número real), la búsqueda del resto del patrón se reinicia cuando finaliza la coincidencia de una subexpresión. Así en el caso del número real, primero buscaba la subexpresión de la parte entera y, si coincidía, buscaba la de la parte decimal en el resto de la cadena que se estaba evaluando. Pero en este caso nos interesa realizar una búsqueda de subexpresiones condicionales, es decir, condicionar cada subexpresión. Veámos con detalle cada parte del patrón. Supongamos que que la contraseña es "aB7C"

  • (?=.*\d) busca algún trozo de texto que tenga cero o más caracteres (representados por .*) y a continuación un primer dígito. El punto representa cualquier caracter, mientras que el asterisco es un cuantificador que dice que lo anterior deberá aparecer cero o más veces. Si esto se aplica al ejemplo, esta subexpresión coincide con "aB7", por lo que hemos encontrado al menos 1 dígito en la contraseña.
  • (?=.*[a-z]). La subexpresión condicional no consume caracteres, es decir, sitúa el cursor otra vez al inicio, por lo que empezaremos otra vez evaluando si ahora hay una letra minúscula. Se obtiene la coincidencia "a" desde la primera letra de la contraseña.
  • (?=.*[A-Z]). Al ser una subexpresión condicional vuelve a evaluar desde el inicio, por lo que ahora coincide con "aB".
  • \w+$. Una vez que las subexpresiones condicionales han resultado todas positivas, pasamos el patrón a toda la cadena, que en este caso se trata de que deben ser caracteres de la clase [A-Za-z0-9_], uno o más de ellos. Debe recordar que el valor ya ha pasado una verificación de la longitud previa acorde con la longitud mínima y máxima declaradas en la definición del campo, por lo que aquí sólo debemos verificar que se cumple el tipo de campo, en este caso, que sean caracteres de la clase [A-Za-z0-9_].

D.N.I. (Documento Nacional de Identidad)

^[1-9]\d*[A-Z]$

El D.N.I. en España se compone de 1 a 8 dígitos y una letra del rango A-Z. La letra es un caracter de control para verificar el número, pero aquí solo realizamos la verificación de caracteres. Con [1-9] declaramos que el número ha de empezar por un dígito del 1 al 9, para evitar el cero o ceros a la izquierda. Luego podemos encontrar cero o más dígitos cualesquiera (\d*). A continuación sólo habrá una letra mayúscula del rango [A-Z]. En este caso no estamos precisando una longitud, pero en la definición del campo haremos que la longitud mínima sea 2 y la máxima 9.

E-mail

^\w+(?:[\-\.]?\w+)*@\w+(?:[\-\.]?\w+)*(?:\.[a-zA-Z]{2,4})+$

Para entender o construir un patrón a veces es necesario ver si podemos subdividir la cadena en partes. Así un email tiene tres zonas diferenciadas: cuenta@dominio.extension, donde cuenta y dominio pueden contener letras mayúsculas "A-Z", minúsculas "a-z", dígitos "0-9", guión bajo "_", guión normal "-" y punto ".", pero no pueden empezar con un guión normal o punto. Además estos dos caracteres si se dan, no pueden aparecer seguidos, es decir, no vale "..", "--", ".-" o "-.". Tampoco deben aparecer al final de la cuenta o dominio, es decir, no vale "abc-@def-.gh" o bien "abc.@def..gh", etc. La extensión debe contener un punto y letras "a-z" o "A-Z" entre 2 y 4 de ellas (son lo ".com", ".es", ".info", etc), aunque pueden repetirse grupos como ".com.es". Partiendo el patrón vemos:

  • \w+: El cuantificador + significa que lo anterior ha de aparecer una o más veces. Así un email debe empezar por uno o más caracteres de la clase [A-Za-z0-9_], incluido el guión bajo.
  • (?:[\-\.]?\w+)*: Ahora tenemos una subexpresión que debe aparecer cero o más veces, según indica el asterisco al final. Se trata de encontrar cero o una vez un guión normal o un punto seguido de uno o más caracteres. Si por ejemplo el email es abc-de.fg@hi.jk será validado, pero no abc--de..fg@hi.jk, con lo que impedimos más de un punto o guión seguidos.
  • @: A continuación debe encontrar una arroba.
  • \w+(?:[\-\.]?\w+)*: Esta es la parte del dominio, patrón que coincide con el anterior a la arroba.
  • (?:\.[a-zA-Z]{2,4})+: Esta parte verifica la extensión, pudiendo haber una o más según indica el + al final. Dentro de la subexpresión encontramos que debe iniciarse con un punto seguido de dos a cuatro letras "a-z", "A-Z".

Veáse que la expresión que encuentra la cuenta o el dominio es \w+(?:[\-\.]?\w+)*. La subexpresión encerrada en (?:) busca guiones y puntos debido a que no se incluyen en la clase de la \w que equivale a [A-Za-z0-9_]. Podríamos pensar en incluir el guión y el punto dentro de estos corchetes: [A-Za-z0-9_\.\-] o bien [\w\.\-], pero esto permitiría varios puntos y guiones seguidos. Por lo tanto la subexpresión busca subcadenas como "-ab" o ".ab". Por ejemplo, si tenemos "xyz-abc.de" en la cuenta o el dominio, el subpatron \w+ del inicio encuentra "xyz", mientras que la subexpresión encuentra "-abc" una vez y ".de" otra segunda vez. Sin embargo si la cuenta o dominio fuera "xyz--abc", la subexpresión no coincidiría en "--abc", pues sólo debe encontrar un guión o un punto o nada antes de un caracter y aquí hay dos guiones seguidos.

Verificación

^si$

Las casillas de verificación son los elementos <input type="checkbox">. Hay que dotarles de un atributo value pues en otro caso el navegador enviará una cadena "on" si la casilla está activada. Yo he optado por incluirles el valor "si" y también verificar esto con una expresión regular tan simple como la expuesta. Aunque también es posible usar cualquier otra cosa, como recibir un "1" si la casilla está activada.

Nombre de persona

^[A-ZÑÇÁÉÍÓÚ]{1}[a-zA-ZñÑçÇáéíóúüÁÉÍÓÚÜ \-]+$

Para el idioma español, agregamos las letras y tildes particulares de este idioma. Así un nombre de persona debe empezar con una letra mayúscula seguido de una o más letras mayúsculas, minúsculas, guión o espacio. El patrón no es muy restrictivo, pues si queremos afinar más tendríamos que dividir este campo en uno para el nombre y otro para el primer apellido (y quizás también para el segundo). Pero en todo caso podemos limitar el envío de dígitos u otros caracteres que no sean letras en un nombre de persona.

Texto simple

^[\wñÑçÇáéíóúüÁÉÍÓÚÜ \-\.\,\?¿\!¡]*$

Podemos limitar los caracteres de un texto para que sólo contengan los mínimos necesarios para enviar un sencillo mensaje de texto. En este caso agregamos a la clase \w, que equivale a [A-Za-z0-9_], las letras en español y la cedilla catalana, así como letras acentuadas y diéresis, espacios, guión, etc. Veáse que los caracteres especiales que se usan en las expresiones regulares deben ser escapados cuando se usen como literales.

Texto Latín Extendido B

^[ -ɏ]*$

Un mensaje de texto que permite caracteres como los equivalentes Unicode desde el u0020 hasta el u024F (en hexadecimal). Comprende todos los caracteres de Europa Occidental. El patrón contiene una clase con un rango que va desde el espacio (u0020) hasta el caracter u0024F. Este último no es representable, por lo que aparecerá un pequeño rectángulo o algo parecido en el navegador, pero supone un rango de caracteres a aceptar. Así se quedarían fueran caracteres como los del alfabeto griego, hebreo, árabe, etc.