Artículos para el Desarrollo Web

Comparar cadenas sin importar mayúsculas ni acentos en PHP

Publicado: 22/12/2019 22:36:06

Volver Comparar cadenas sin importar mayúsculas ni acentos en PHP
Comparar texto sin tener en cuenta la diferencia entre las mayúsculas y minúsculas, ni la diferencia entre letras acentuadas o no es a veces una necesidad que no es fácil de resolver.

Este artículo presenta una clase llamada "CompareText" que hará muy sencillo este cometido y además de manera rápida.

Por ejemplo, si queremos comparar la palabra Valencia con València, en una comparación normal nos dirá que son palabras diferentes incluso utilizando la función 'strcasecmp' de PHP:
        
strcasecmp('Valencia', 'Valéncia'); // Devuelve -94, la primera es menor que la segunda

pero con
        
CompareText::icmp('Valencia', 'Valéncia'); // Devuelve 0, son iguales


Aquí la clave está en los acentos, es decir, que se consideren iguales 'A', 'á', 'à', etc.

Vamos a utilizar las ventajas de los arrays asociativos de PHP para obtener un valor numérico igual para letras "textualmente iguales":
        $this->alpha = [
            'F' => 65, 'f' => 65,
            'G' => 66, 'g' => 66,
            'H' => 67, 'h' => 67,
            'J' => 68, 'j' => 68,
            'K' => 69, 'k' => 69,
            'L' => 70, 'l' => 70,
            'M' => 71, 'm' => 71,
            'Q' => 72, 'q' => 72,
            'R' => 73, 'r' => 73,
            'T' => 74, 't' => 74,
            'V' => 75, 'v' => 75,
            'W' => 76, 'w' => 76,
            'X' => 77, 'x' => 77,
            'A' => 78, 'a' => 78,
            'Á' => 78, 'á' => 78, 'À' => 78, 'à' => 78, 'Â' => 78, 'â' => 78,
                'Ã' => 78, 'ã' => 78, 'Ä' => 78, 'ä' => 78, 'Å' => 78, 'å' => 78,
                'Æ' => 78, 'æ' => 78,

Como podemos ver en este extracto, de la clase, hay más de 10 versiones de la letra 'a' que deberían considerarse iguales, para ello cada vez que se comparen dos letras, el método CompareText::icmp($s1, $s2), llamará al método CompareText::ipos($c):
    

    /**
     * Compara dos cadenas sin tener en cuenta la diferencia entre
     * mayúsculas y minúsculas ni los signos.
     * Devuelve 0 si so iguales.
     * -1 si la primera es menor que la segunda.
     * 1 si la primera es mayor que la segunda.
     * @param string $s1 Primera cadena que se comparará.
     * @param string $s2 Segunda cadena que se comparará.
     * @return int
     */
    public function icmp($s1, $s2) {
        if (mb_strlen($s1) > mb_strlen($s2)) {
            return 1;
        } else if (mb_strlen($s1) < mb_strlen($s2)) {
            return -1;
        } else {
            for ($i = 0; $i < mb_strlen($s1); $i++) {
                $j = $this->ipos(mb_substr($s1, $i, 1)); // Buscamos la posición de la letra de la primera cadena
                $k = $this->ipos(mb_substr($s2, $i, 1)); // Buscamos la posición de la letra de la segunda cadena
                if ($j > $k) {
                    return 1;
                } else if ($j < $k) {
                    return -1;
                }
            }
        }

        return 0;
    }


La función se explica por sí sola, en ella, se comparan parejas de caracteres con la función:
    

    /**
     * Devuelve la posición de una letra en un criterio relativo de comparación
     * que ignora diferencias entre mayúsculas y minúsculas y sus signos.
     * @param string $c Cadena conteniendo un único carácter.
     * @return int
     */
    public function ipos($c) {
        // Obsérvese que si la letra no existe en el array se devuelve su
        // código UTF-8 a través de la función mb_ord($c)
        return $this->alpha[$c] ?? mb_ord($c);
    }


que utiliza las ventajas de los arrays asociativos de PHP para obtener un valor numérico representativo de la letra que se está comparando, además gracias a esta implementación, la velocidad de ejecución es alta.

Me gustaría destacar que en esta clase se han usado funciones mb_nnn(...) de PHP, que permiten el manejo de cadenas UTF-8 con enorme facilidad.

Descargar y probar el ejemplo
Volver