28 votos

¿Glob() no puede encontrar nombres de archivos con caracteres multibyte en Windows?

Estoy escribiendo un gestor de archivos y necesito escanear directorios y lidiar con el renombramiento de archivos que pueden tener caracteres multibyte. Estoy trabajando en ello localmente en Windows/Apache PHP 5.3.8, con los siguientes nombres de archivos en un directorio:

  • nombre de archivo.jpg
  • имяфайла.jpg
  • file件name.jpg
  • פילענאַמע.jpg
  • 文件名.jpg

Las pruebas en un servidor UNIX vivo funcionaron bien. Probando localmente en Windows usando glob('./path/*') devuelve sólo la primera, filename.jpg .

Utilizando scandir() se devuelve al menos el número correcto de archivos, pero obtengo nombres como ?????????.jpg (nota: son signos de interrogación normales, no el carácter �.

Acabaré necesitando escribir una función de "búsqueda" para buscar recursivamente en todo el árbol los nombres de archivo que coincidan con un patrón o con una determinada extensión de archivo, y supuse que glob() sería la herramienta adecuada para ello, en lugar de escanear todo los archivos y hacer la coincidencia de patrones y la construcción de Array en el código de la aplicación. Estoy abierto a sugerencias alternativas si es necesario.

Asumiendo que se trataba de un problema común, inmediatamente busqué en Google y en Stack Overflow y no encontré nada relacionado. ¿Es un problema de Windows? ¿Defecto de PHP? ¿Cuál es la solución: hay algo que pueda hacer?

Adenda: No estoy seguro de qué relación tiene esto, pero file_exists() también regresa FALSE para estos archivos, pasando la ruta absoluta completa (usando el Notepad++, el archivo PHP en sí mismo está codificado en UTF-8 sin BOM). Estoy seguro de que la ruta es correcta, ya que los archivos vecinos sin caracteres multibyte devuelven TRUE .

EDITAR : glob() puede encontrar un archivo llamado filename-äöü.jpg . Anteriormente en mi .htaccess archivo, tenía AddDefaultCharset utf-8 que no había tenido en cuenta antes. filename-äöü.jpg estaba imprimiendo como filename-���.jpg . El único efecto que parece tener la eliminación de esa línea htaccess es que ahora el nombre del archivo se imprime normalmente.

He borrado el .htaccess completamente, y este es mi prueba real script en su totalidad (he cambiado un par de nombres de archivo del post original):

print_r(scandir('./uploads/')); 
print_r(glob('./uploads/*'));

Salida local en Windows:

Array
(
    [0] => .
    [1] => ..
    [2] => ??? ?????.jpg
    [3] => ???.jpg
    [4] => ?????????.jpg
    [5] => filename-äöü.jpg
    [6] => filename.jpg
    [7] => test?test.jpg
)
Array
(
    [0] => ./uploads/filename-äöü.jpg
    [1] => ./uploads/filename.jpg
)

Salida en el servidor remoto UNIX:

Array
(
    [0] => .
    [1] => ..
    [2] => filename-äöü.jpg
    [3] => filename.jpg
    [4] => test이test.jpg
    [5] => имя файла.jpg
    [6] => פילענאַמע.jpg
    [7] => 文件名.jpg
)
Array
(
    [0] => ./uploads/filename-äöü.jpg
    [1] => ./uploads/filename.jpg
    [2] => ./uploads/test이test.jpg
    [3] => ./uploads/имя файла.jpg
    [4] => ./uploads/פילענאַמע.jpg
    [5] => ./uploads/文件名.jpg
)

Dado que se trata de un servidor diferente, independientemente de la plataforma - la configuración podría ser diferente, así que no estoy seguro de qué pensar, y no puedo totalmente pin en Windows todavía (podría ser mi instalación de PHP, la configuración ini, o la configuración de Apache). ¿Alguna idea?

7voto

virmaior Puntos 233

Parece que la función glob() depende de cómo se construyó su copia de PHP y de si se compiló con una API WIN32 compatible con el código único (no creo que el builid estándar lo sea).

Cf. http://www.rooftopsolutions.nl/blog/filesystem-encoding-and-PHP

Extracto de los comentarios sobre el artículo:

Philippe Verdy 2010-09-26 8:53 am

La salida de su instalación de PHP en Windows es fácil de explicar : usted instaló la versión incorrecta de PHP, y usó una versión no compilada para usar la versión Unicode de la API Win32. Por esta razón las llamadas al sistema de archivos usadas por PHP usarán la API "ANSI" heredada y así las bibliotecas C/C++ enlazadas con esta versión de PHP intentarán primero convertir su cadena PHP codificada en UTF-8 a la página de código local "ANSI seleccionada en el entorno de ejecución (vea el comando CHCP antes de iniciar PHP desde una ventana de línea de comandos)

Su versión de Windows NO ES PROBABLEMENTE responsable de esta extraña cosa rara. En realidad, es SU versión de PHP la que no está compilada correctamente, y que utiliza la versión ANSI heredada de la API Win32 (para compatibilidad con las versiones heredadas de 16 bits de Windows 95/98 cuyo soporte del sistema de archivos en el kernel en realidad no tenía soporte directo para Unicode, sino que utilizaba una capa de conversión interna para convertir Unicode en la codificación local ANSI antes de utilizar la versión ANSI real de la API).

Recompilar PHP usando la opción del compilador para usar la versión UNICODE de la API Win32 (que debería ser el valor por defecto hoy en día, y de todos modos siempre por defecto para PHP instalado en un servidor que NUNCA será Windows 95 o Windows 98...)

Entonces Windows podrá almacenar nombres de archivo codificados en UTF-16 (incluso en volúmenes FAT32, aunque, en estos volúmenes, también generará un nombre corto aliasado en formato 8.3 usando la codificación por defecto del sistema de archivos del sistema de archivos, algo que puede evitarse en los volúmenes NTFS).

Todo lo que describes son problemas de PHP (portación incorrecta a Windows, o identificación incorrecta de la versión del sistema en tiempo de ejecución) : vuelva a leer los archivos README que vienen con las fuentes de PHP explicando las banderas de compilación. Realmente creo que el makefile en Windows debería ser capaz de configurar y autodetectar si realmente necesita usar SOLO la versión versión ANSI de la API. Si usted está compilando para un servidor, asegúrese de que el Asegúrese de que el Configure script detecte efectivamente el soporte completo de la versión UNICODE. soporte de la versión UNICODE de la aPI Win32 y la usará cuando compilar PHP y al seleccionar las bibliotecas de tiempo de ejecución para enlazar.

Yo uso PHP en Windows, correctamente compilado, y no conozco en absoluto los problemas que citas en tu artículo.

Olvidemos ahora para siempre estas versiones no UNICODE de la API Win32 API (que utilizan de forma inconsistente la codificación local ANSI para el la interfaz gráfica de Windows, y la página de código OEM para las APIs del sistema de archivos, las APIs compatibles con DOS/BIOS, las APIs de la consola): estas versiones no Unicode son incluso MUCHO más lentas y costosas que las versiones versiones Unicode de las APIs, porque en realidad están traduciendo la página de código a Unicode antes de utilizar las APIs Unicode principales (la situación en los núcleos basados en Windows NT es exactamente la inversa de la situación en las versiones de Windows basadas en un extensor virtual de DOS, como como Windows 95/98/ME).

Cuando no se utiliza la versión nativa de la API, la llamada a la API pasará a través de una capa de desconexión que transcodificará las cadenas entre Unicode y una de las páginas de código OEM seleccionadas por ANSI o CHCP, o la página de códigos OEM indicada en el sistema de archivos: esto requiere una asignación de memoria memoria temporal adicional dentro de la versión no nativa de la API Win32 API. Esto toma tiempo adicional para convertir las cosas antes de hacer el trabajo real llamando a la API nativa.

En resumen: el binario de PHP que instale en Windows DEBE ser diferente dependiendo de si lo compiló para Windows 95/98/SE (o la antigua capa de emulación Win16s para Windows 3.x, que tenía un soporte mínimo de soporte de UTF-8, sólo para soportar los subconjuntos de Unicode usados por los códigos ANSI y OEM seleccionados al iniciar Windows desde un extensor DOS DOS) o si fue compilado para cualquier otra versión de Windows basada en el kernel de NT.

La mejor prueba de que esto es un problema de PHP y no de Windows, es que sus resultados extraños NO se producen en otros lenguajes como C#, Javascript, VB, Perl, Ruby... PHP tiene un historial muy malo en el seguimiento de versiones (y demasiadas peculiaridades históricas del código fuente y suposiciones suposiciones erróneas que deberían estar desactivadas hoy en día, y una biblioteca inconsistente que ha heredado todas esas peculiaridades hechas inicialmente en versiones antiguas de PHP para versiones antiguas de Windows que incluso ya no son soportadas oficialmente por Microsoft o incluso por el propio PHP).

En otras palabras : ¡RTFM! O descargue e instale una versión binaria de PHP para Windows precompilada con la configuración correcta : realmente creo que PHP debería distribuir binarios para Windows ya compilados por por defecto para la versión Unicode de la API Win32, y usando la versión Unicode de las librerías C/C++ : internamente el código PHP convertirá sus cadenas UTF-8 a UTF-16 antes de llamar a la API Win32, y de vuelta de UTF-16 a UTF-8 al recuperar los resultados de Win32, en lugar de convertir las cadenas UTF-8 internas de PHP a la página de código OEM local (para las llamadas al sistema de archivos) o la codificación local ANSI (para todas las demás APIs Win32, incluyendo el registro o el proceso).

-1voto

CarcaBot Puntos 19

Intente

<?php
header('Content-Type: text/html; charset=iso-8859-1'); // or charset=utf-8');
print_r(glob('./uploads/*'));
?>

-1voto

relu Puntos 48

Prueba a poner mb_internal_encoding() a " UTF-8 " antes de utilizar glob

mb_internal_encoding("UTF-8");
print_r(glob('./uploads/*'));

-1voto

Mihai Popescu Puntos 11

No he tocado PHP desde hace 3 o 4 años, pero tal vez esto puede ayudar :

pathinfo() es consciente de la configuración regional, por lo que para que analice correctamente una ruta que contenga caracteres multibyte, la configuración regional correspondiente debe establecerse mediante la función setlocale()

Y algunos enlaces directos:

pathinfo - leer la segunda nota

sobre setlocale

(Creo que tu problema viene del escaneo de los directorios, y no del propio código de visualización o de las cabeceras, ya que Chrome o firefox, si recuerdo bien, pueden manejar los caracteres Unicode).

-1voto

Pierre Puntos 560

PHP en Windows aún no utiliza la API Unicode. Así que tiene que usar la codificación en tiempo de ejecución (cualquiera que sea) para poder tratar con conjuntos de caracteres no ascii.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X