2
<?php
    $pal="niño";
    $pal=mb_strtoupper($pal);
    $long=strlen($pal);
    for($i=0;$i<$long;$i++){

            $arr[$i]=$pal[$i];

    }
    foreach($arr as $valor){
        echo"&nbsp_&nbsp";
    }
?>

**este es el resultado: _ _ _ _ _

en vez de salir 4 guiones como letras tiene la palabra niño salen 5 guiones **

javier
  • 65
  • 7
  • Necesitas configurar bien tu **entorno**. [Por favor lee aquí **detenidamente**, sin prisas](https://es.stackoverflow.com/a/59510/29967) y te ahorrarás muchos dolores de cabeza. No recurras a soluciones fáciles, aplicando funciones de codificación sobre cada dato y cada variable. – A. Cedano Dec 08 '18 at 12:45
  • Por cierto no es _echo "valor"_ es _echo "$valor"_ y si, como dice @A.Cedano configura bien tu entorno, el código está bien y puedes usar la Ñ en los mismos lugares en los que uses cualquier otra letra ;-) – Neoniet Dec 08 '18 at 12:52
  • Gracias Neoniet por el apunte, de todas maneras el entorno esta configurado correctamente como dice el enlace que me envió @A.Cedano. El php.ini tiene la configuración default_charset="UTF-8" por lo que internal_encoding usa la misma configuración de caracteres. Asi que no se donde esta el error¿?? – javier Dec 08 '18 at 14:53
  • y si pruebas añadiendo al inicio de tu documento despues de la etiqueta –  Dec 08 '18 at 15:06

3 Answers3

3

Esto ocurre porque, como dice la nota en el Manual de PHP:

strlen() devuelve el número de bytes en lugar del número de caracteres de un string.

Debido a la ñ, tu cadena tiene 5 bytes, no 4.

Otras soluciones son posibles:

Tres posibilidades

1. Usar mb_strlen

Sólo que en muchas instalaciones de PHP no existe. Y puede que haga falta instalarlo previamente:

<?php    
    $pal="niño";
    $long=mb_strlen($pal);
    echo $long;
?>

2. Usar una expresión regular

<?php
    $pal="niño";
    $long = preg_match_all( '(.)su', $pal, $matches );
    echo $long;
?>

3. Decodificar la cadena

<?php
    $pal="niño";
    $long = strlen( utf8_decode( $pal ) );
    echo $long;
?>

Caso curioso: leer por caracteres

Curiosamente, si se intenta leer la cadena como si fuera un array, caracter por caracter, queda demostrado que la codificación no aplica de manera interna a los elementos del array.

Según se explica en este artículo, para hacer esto es preciso crear una función propia que maneje los caracteres multibyte de la cadena:

function mb_str_split($str) {
   // split multibyte string in characters
   // Split at all positions, not after the start: ^
   // and not before the end: $
   $pattern = '/(?<!^)(?!$)/u';
   return preg_split($pattern,$str);
}

Ayudándonos de esa función sí es posible imprimir bien los caracteres. Entonces el código quedaría así:

$arrPal = mb_str_split($pal);//str_split($pal,4);
for($i=0;$i<$long;$i++){ 
    echo " $arrPal[$i] ";
} 

O, podemos usar un for mejorado:

foreach ($arrPal as $char) {
 echo $char;
}

En ambos casos la salida sería correcta.

Es curioso e interesante lo que ocurre aquí y sería bueno quizá plantearlo como una pregunta independiente.

A. Cedano
  • 86,578
  • 19
  • 122
  • 221
  • Gracias por la respuesta, Pero si luego intento mostrar la variable como un array con un for for($i=0;$i – javier Dec 08 '18 at 18:34
  • @javier ¿tienes bien configurado tu entorno a través de un `header`, de php.ini u otro?. Es extraño que ocurra eso. – A. Cedano Dec 08 '18 at 18:39
  • header("Content-Type: text/html;charset=utf-8"); El php.ini tiene default_charset UTF-8 – javier Dec 08 '18 at 18:52
  • @javier pues sí, probando tu código imprime el carácter inspector cuando se trata la cadena como un array. Es curioso, porque si se imprime la cadena entera no hay problema, pero si se obtiene como array no respecta la codificación. Voy a echar un vistazo a eso. – A. Cedano Dec 08 '18 at 18:53
  • Gracias @A.Cedano ,a ver si hay solución, conseguí que mostrara n i � o añadiendo: `$pal=iconv("UTF-8", "ISO-8859-1", $pal);` Pero no consigo que me muestre el valor: niño – javier Dec 08 '18 at 19:27
  • @javier he aportado una solución (revisa el apartado **Caso curioso: leer por caracteres** en la respuesta editada, al final). No sé si sea la mejor opción, pero resuelve el problema. De todos modos me sorprendió este comportamiento al tratar la cadena como elementos de un array y sería interesante plantear el caso en una pregunta dedicada totalmente a esa situación tan curiosa, para tener puntos de vista de otros programadores. – A. Cedano Dec 08 '18 at 19:34
  • Muchas gracias @A.Cedano por tu respuesta, la verdad que así funciona, me resulta un poco complicado entender el `pattern`. La verdad es que estaría interesante plantearla como una pregunta independiente, si me surgen mas problemas lo haré, un saludo. – javier Dec 08 '18 at 20:08
  • Javier la función la obtuve del enlace y en las notas en inglés está explicado el *pattern*. Yo conozco muy poco sobre REGEX, había por aquí un maestro en ese tema que era @Mariano sólo que hace tiempo que no le veo por Stackoverflow. – A. Cedano Dec 08 '18 at 20:10
0

A mi se me ocurre que ocupes al inicio una instrucción header para declarar que codificación va a tener el mismo; quedando de este modo.

Además de eso le coloqué un Content-Type: text/plain por que solo vas a devolver un texto sin ninguna clase de formato predeterminado o de tipo HTML

<?php

header('Content-Type: text/plain; charset=utf8');


    $pal="niño";
    $pal=mb_strtoupper($pal);
    $long=strlen($pal);
    for($i=0;$i<$long;$i++){

            $arr[$i]=$pal[$i];

    }
    foreach($arr as $valor){
        echo"$valor";
    }
  • Gracias por la respuesta,@shadow y perdona porque el resultado de la pregunta anterior era correcto no fallaba nada, acabo de editar la pregunta . Realmente la pregunta de ahora si que falla – javier Dec 08 '18 at 15:52
0

Como puedes ver en la documentación, strlen devuelve el número de bytes en lugar del número de caracteres de un string. Por lo tanto no vale para caracteres unicode.

La solución es utilizar mb_strlen

<?php    
$pal="niño";
$long=mb_strlen($pal);
echo"$long";
?>
asantana o
  • 897
  • 1
  • 9
  • 16
  • Gracias por la respuesta, Pero si luego intento mostrar la variable como un array con un for for($i=0;$i – javier Dec 08 '18 at 18:36