¿Cómo uso las funciones del sistema de archivos en PHP, usando cadenas UTF-8?

No puedo usar mkdir para crear carpetas con caracteres UTF-8.

 

Pero cuando navego por esta carpeta en Windows Explorer, el nombre de la carpeta es así:

 Depósito 

¿Que debería hacer?

Simplemente urlencode la cadena deseada como un nombre de archivo. Todos los caracteres devueltos desde urlencode son válidos en nombres de archivo (NTFS / HFS / UNIX), luego puede urldecode los nombres de los archivos nuevamente en UTF-8 (o en cualquier encoding en la que se encuentren).

Advertencias (todas se aplican a las siguientes soluciones también):

  • Después de la encoding url, el nombre de archivo debe ser menor a 255 caracteres (probablemente bytes).
  • UTF-8 tiene múltiples representaciones para muchos caracteres (usando caracteres combinados). Si no normaliza su UTF-8, puede tener problemas para buscar con glob o reabrir un archivo individual.
  • No puede confiar en scandir o funciones similares para alfa-sorting. Debe urldecode los nombres de los archivos y luego utilizar un algoritmo de clasificación que tenga en cuenta el UTF-8 (y las intercalaciones).

Peores soluciones

Las siguientes son soluciones menos atractivas, más complicadas y con más advertencias.

En Windows, el contenedor del sistema de archivos PHP espera y devuelve cadenas ISO-8859-1 para los nombres de archivos / directorios. Esto te da dos opciones:

  1. Use UTF-8 libremente en sus nombres de archivo, pero entienda que los caracteres que no son ASCII aparecerán incorrectos fuera de PHP. Un carácter no UTC-8 ASCII se almacenará como múltiples caracteres ISO-8859-1 individuales . Por ejemplo, aparecerá como ó en Windows Explorer.

  2. Limite sus nombres de archivo / directorio a caracteres representables en ISO-8859-1 . En la práctica, pasará sus cadenas UTF-8 a través de utf8_decode antes de usarlas en las funciones del sistema de archivos, y pasará las entradas que scandir le brinda a través de utf8_encode para obtener los nombres de archivo originales en UTF-8.

¡Expectativas en abundancia!

  • Si un byte transferido a una función del sistema de archivos coincide con un carácter de sistema de archivos de Windows no válido en ISO-8859-1, no tiene suerte.
  • Windows puede usar una encoding que no sea ISO-8859-1 en locales no ingleses. Supongo que generalmente será uno de ISO-8859- #, pero esto significa que necesitarás usar mb_convert_encoding lugar de utf8_decode .

Esta pesadilla es la razón por la que probablemente solo deberías transcribir para crear nombres de archivo.

En Unix y Linux (y posiblemente también en OS X), la encoding actual del sistema de archivos viene dada por el parámetro LC_CTYPE locale (ver función setlocale() ). Por ejemplo, puede evaluar algo como en_US.UTF-8 que significa que la encoding es UTF-8. Luego, los nombres de los archivos y sus rutas se pueden crear con fopen() o recuperados por dir() con esta encoding.

En Windows, PHP funciona como un “progtwig no compatible con Unicode”, luego los nombres de los archivos se convierten desde el UTF-16 utilizado por el sistema de archivos (Windows 2000 y posterior) a la “página de códigos” seleccionada. El panel de control “Configuración regional y de idioma”, el panel de tabs “Formatos” configura la página de códigos recuperada mediante la opción LC_CTYPE , mientras que “Administrativo -> Idioma para progtwigs no Unicode” establece la página de códigos de traducción para los nombres de archivo. En los países occidentales, el parámetro LC_CTYPE se evalúa como language_country.1252 donde 1252 es la página de códigos, también conocida como “encoding Windows-1252”, que es similar (pero no exactamente igual) a ISO-8859-1. En Japón, la página de códigos 932 generalmente se establece en su lugar, y así sucesivamente para otros países. En PHP, puede crear archivos cuyo nombre se pueda express con la página de códigos actual. Viceversa, los nombres de archivo y las rutas recuperadas del sistema de archivos se convierten de UTF-16 a bytes utilizando la página de códigos actual que mejor se ajusta .

Este mapeo es aproximado, por lo que algunos personajes pueden ser destrozados de una manera impredecible. Por ejemplo, Caffé Brillì.txt sería devuelto por dir() como la cadena PHP Caff\xE9 Brill\xEC.txt como se esperaba si la página de códigos actual es 1252, mientras que devolvería el aproximado Caffe Brilli.txt en un sistema japonés porque las vocales acentuadas faltan en la página de códigos 932 y luego se reemplazan por las vocales sin acentos que mejor se ajustan. Los caracteres que no se pueden traducir en absoluto se recuperan como ? (signo de interrogación). En general, en Windows no existe una forma segura de detectar dichos artefactos.

Más detalles están disponibles en mi respuesta al error de PHP no. 47096 .

El problema es que Windows usa utf-16 para las cadenas del sistema de archivos, mientras que Linux y otros usan diferentes conjuntos de caracteres, pero a menudo utf-8. Proporcionó una cadena utf-8, pero esto se interpreta como otra encoding de conjunto de caracteres de 8 bits en Windows, tal vez Latin-1, y luego el carácter no ascii, que está codificado con 2 bytes en utf-8, se maneja como si fue 2 caracteres en Windows.

Una solución normal es mantener el código fuente al 100% en ascii, y tener cadenas en otro lugar.

PHP 7.1 admite nombres de archivos UTF-8 en Windows sin tener en cuenta la página de códigos OEM.

Gracias.

Con la extensión PHP de com_dotnet , puede acceder a Scripting.FileSystemObject Windows y luego hacer todo lo que desee con los nombres de archivos / carpetas UTF-8.

Empaqueté esto como un contenedor de flujo PHP, por lo que es muy fácil de usar:

https://github.com/nicolas-grekas/Patchwork-UTF8/blob/lab-windows-fs/class/Patchwork/Utf8/WinFsStreamWrapper.php

Primero verifique que la extensión com_dotnet esté habilitada en su php.ini luego habilite el contenedor con:

 stream_wrapper_register('win', 'Patchwork\Utf8\WinFsStreamWrapper'); 

Finalmente, use las funciones a las que está acostumbrado (mkdir, fopen, rename, etc.), pero prefija su ruta con win://

Por ejemplo:

  

Puede usar esta extensión para resolver su problema: https://github.com/kenjiuno/php-wfio

 $file = fopen("wfio://多国語.txt", "rb"); // in UTF-8 .... fclose($file); 

Pruebe el Ayudante de CodeIgniter Text desde este enlace Lea sobre la función convert_accented_characters (), se puede personalizar

Mi conjunto de herramientas para usar el sistema de archivos con UTF-8 en Windows O Linux a través de PHP y compatible con el archivo de verificación .htaccess existe:

 function define_cur_os(){ //$cur_os=strtolower(php_uname()); $cur_os=strtolower(PHP_OS); if(substr($cur_os, 0, 3) === 'win'){ $cur_os='windows'; } define('CUR_OS',$cur_os); } function filesystem_encode($file_name=''){ $file_name=urldecode($file_name); if(CUR_OS=='windows'){ $file_name=iconv("UTF-8", "ISO-8859-1//TRANSLIT", $file_name); } return $file_name; } function custom_mkdir($dir_path='', $chmod=0755){ $dir_path=filesystem_encode($dir_path); if(!is_dir($dir_path)){ if(!mkdir($dir_path, $chmod, true)){ //handle mkdir error } } return $dir_path; } function custom_fopen($dir_path='', $file_name='', $mode='w'){ if($dir_path!='' && $file_name!=''){ $dir_path=custom_mkdir($dir_path); $file_name=filesystem_encode($file_name); return fopen($dir_path.$file_name, $mode); } return false; } function custom_file_exists($file_path=''){ $file_path=filesystem_encode($file_path); return file_exists($file_path); } function custom_file_get_contents($file_path=''){ $file_path=filesystem_encode($file_path); return file_get_contents($file_path); } 

Recursos adicionales