Dividir un string en caracteres parece una tarea sencilla, y lo es. mientras un carácter sea solo un carácter.
Como así?
En unicode existen caracteres que se combinan con otros, ejemplo de estos son los caracteres de acento. De esta forma a simple vista un carácter puede parecer un solo símbolo pero estar formado por dos o mas.Por ejemplo la letra "a" con tilde puede ser un solo carácter (u+00e1), pero puede darse el caso que sea una letra a + una tilde (a+') que son dos caracteres (u+0061 y u+0301 respectivamente).
Esto es un problema, en especial para una aplicación que necesite acceder a cada uno de los caracteres de una palabra como en los crucigramas, ahorcado, sopas de letras, etc.
Por ejemplo en mi juego de sopa de letras a veces las palabras en la cuadricula quedaban en la posición incorrecta. El problema, algunas palabras usaban mas espacio debido a los caracteres de combinación (eje. "mamá" ocupando 5 cuadros = m | a | m | a | ' |).
Que puede salir mal
Para conocer el largo de una cadena de texto normalmente usamos la propiedad length... error, ésta solo da resultados correctos si cada carácter en el texto esta formado por un solo codepoint.- alert("mamá".length)
Gracias a un enlace en MDN (Mozilla Developer Network) encontre esta solución. para obtener el largo correcto del string contamos los caracteres que no son marcas de combinación. a continuación la función (con algunas modificaciones).
- // regexp = rangos de caracteres correspondientes a marcas de combinación
- rCombiningMarks = /[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F\u0483-\u0489\u0591-\u05BD]/g;
- function stringLength(string) {
- // iniciar un contador de caracteres en 0
- var length = 0;
- // por cada caracter en string
- for (var i = 0, l = string.length; i < l; i++) {
- // si no es una marca de combinación
- if (! rCombiningMarks.test(string.charAt(i)) )
- // incrementar el contador de caracteres
- length += 1
- }
- return length;
- }
- alert(stringLength("mamá"));
Como dividir un string?
Podemos usar un método similar al anterior para dividir un string:
- // regexp = rangos de caracteres correspondientes a marcas de combinación
- rCombiningMarks = /[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F\u0483-\u0489\u0591-\u05BD]/g;
- function splitString(string) {
- // iniciar un array de caracteres
- var chars = [],
- lastChar;
- // por cada caracter en string
- for (var i = 0, l = string.length; i < l; i++) {
- // si no es una marca de cominación
- if (! rCombiningMarks.test(string.charAt(i)) ) {
- // Agregar al array de caracteres
- chars.push(string.charAt(i));
- }
- // de lo contrario
- else {
- // Concatenar con ultimo caracter en el array
- var lastChar = chars[chars.length - 1];
- chars[chars.length - 1] = lastChar + string.charAt(i);
- }
- }
- return chars;
- }
- // TEST
- alert("con string.split: "+("mamá".split("").join(" | ")));
- alert("con splitString: " + splitString("mama").join(" | "));
Conclusión
Aun cuando javascript nos provee varios métodos útiles para el manejo de strings, números, arrays y objetos, estos no siempre nos darán los resultados deseados. debemos probar nuestro código para estar seguros que se comporta de la forma correcta.Algún otro método/función nativa de javascript de la que no hay que confiar. Déjalo en los comentarios.
No hay respuestas a “Dividir correctamente un string en caracteres. No con string.split("")”
Deja una respuesta