web-dev-qa-db-fra.com

Comment utiliser les fonctions de système de fichiers en PHP, en utilisant des chaînes UTF-8?

Je ne peux pas utiliser mkdir pour créer des dossiers avec des caractères UTF-8:

<?php
$dir_name = "Depósito";
mkdir($dir_name);
?>

lorsque je navigue dans ce dossier dans l'Explorateur Windows, le nom du dossier se présente comme suit:

Depósito

Que devrais-je faire?

J'utilise php5

31
Acacio Nerull

Just urlencode la chaîne souhaitée en tant que nom de fichier.Tous les caractères renvoyés par urlencode sont valides dans les noms de fichiers (NTFS/HFS/UNIX), vous pouvez donc simplement urldecode les noms de fichiers revenir à UTF-8 (ou le codage dans lequel ils étaient).

Mises en garde (toutes s'appliquent également aux solutions ci-dessous): 

  • Après le codage d'URL, le nom du fichier doit comporter moins de 255 caractères (probablement des octets).
  • UTF-8 a représentations multiples pour plusieurs caractères (combinaison de caractères). Si vous ne normalisez pas votre UTF-8, vous aurez peut-être de la difficulté à rechercher avec glob ou à rouvrir un fichier individuel.
  • Vous ne pouvez pas compter sur scandir ou des fonctions similaires pour le tri alpha. Vous devez urldecode les noms de fichiers, puis utiliser un algorithme de tri prenant en compte UTF-8 (et des classements).

Pires solutions

Les solutions suivantes sont moins attrayantes, plus compliquées et avec plus de mises en garde.

Sous Windows, l’enveloppe de système de fichiers PHP attend et renvoie les chaînes ISO-8859-1 pour les noms de fichier/répertoire. Cela vous donne deux choix:

  1. Utilisez librement UTF-8 dans vos noms de fichiers, mais comprenez que les caractères non-ASCII apparaîtront inexact en dehors de PHP. Un caractère UTF-8 non-ASCII sera stocké sous forme de plusieurs un seul ISO-8859-1 caractères. Par exemple. ó apparaîtra en tant que ó dans l'Explorateur Windows.

  2. Limitez les noms de fichiers/répertoires à caractères pouvant être représentés dans ISO-8859-1 . En pratique, vous passerez vos chaînes UTF-8 par le biais de utf8_decode avant de les utiliser dans des fonctions de système de fichiers, puis vous ferez passer les entrées scandir par utf8_encode pour obtenir les noms de fichier d'origine en UTF-8.

Mises en garde à gogo!

  • Si un octet transmis à une fonction de système de fichiers correspond à un caractère non valide du système de fichiers Windows dans ISO-8859-1, vous n'avez pas de chance.
  • Windows peut [] utiliser un codage autre que ISO-8859-1 dans des environnements locaux autres que l'anglais. Je suppose que ce sera généralement l'un des ISO-8859- #, mais cela signifie que vous devrez utiliser mb_convert_encoding au lieu de utf8_decode.

Ce cauchemar est la raison pour laquelle vous devriez probablement juste translittérer _ créer des noms de fichiers.

23
Steve Clay

Sous Unix et Linux (et éventuellement sous OS X également), le codage actuel du système de fichiers est donné par le paramètre régional LC_CTYPE (voir la fonction setlocale()). Par exemple, il se peut que quelque chose comme en_US.UTF-8 indique que le codage est UTF-8. Ensuite, les noms de fichiers et leurs chemins peuvent être créés avec fopen() ou récupérés par dir() avec cet encodage.

Sous Windows, PHP fonctionne comme un "programme non compatible avec Unicode", puis les noms de fichier sont convertis en aller et retour de l'UTF-16 utilisé par le système de fichiers (Windows 2000 et versions ultérieures) en "page de code" sélectionnée. . Le panneau de configuration "Options régionales et linguistiques", panneau d'onglets "Formats" définit la page de code récupérée par l'option LC_CTYPE, tandis que "Administrateur -> Langue pour les programmes non Unicode" définit la page de code de traduction pour les noms de fichier. Dans les pays occidentaux, le paramètre LC_CTYPE correspond à quelque chose comme language_country.1252, où 1252 est la page de code, également appelée «codage Windows-1252», qui est similaire (mais pas exactement identique) à ISO-8859-1. Au Japon, la page de codes 932 est généralement définie à la place et ainsi de suite pour les autres pays. Sous PHP, vous pouvez créer des fichiers dont le nom peut être exprimé avec la page de code actuelle. Inversement, les noms de fichiers et les chemins extraits du système de fichiers sont convertis d'octets UTF-16 à l'aide de la page de codes actuelle "optimale" .

Ce mappage étant approximatif, il est possible que certains caractères soient mutilés de manière imprévisible. Par exemple, Caffé Brillì.txt serait renvoyé par dir() en tant que PHP chaîne Caff\xE9 Brill\xEC.txt comme prévu si la page de code actuelle est 1252, alors qu'elle renverrait le Caffe Brilli.txt approximatif sur un système japonais car les voyelles accentuées sont absentes de la page de code 932 et puis remplacés par leurs voyelles "les mieux adaptées" non accentuées. Les caractères qui ne peuvent pas du tout être traduits sont récupérés sous la forme ? (point d'interrogation). En général, sous Windows, il n’existe aucun moyen sûr de détecter de tels artefacts.

Plus de détails sont disponibles dans ma réponse au bogue n ° PHP. 47096 .

12
Umberto Salsi

PHP 7.1 prend en charge les noms de fichiers UTF-8 sous Windows, sans tenir compte de la page de codes OEM.

8
Anatol Belski

Le problème est que Windows utilise utf-16 pour les chaînes de système de fichiers, alors que Linux et d'autres utilisent des jeux de caractères différents, mais souvent utf-8. Vous avez fourni une chaîne utf-8, mais celle-ci est interprétée comme un autre codage de jeu de caractères 8 bits sous Windows, par exemple Latin-1, puis le caractère non ascii, codé avec 2 octets dans utf-8, est traité comme suit: s'il s'agissait de 2 caractères sous Windows.

Une solution normale consiste à conserver votre code source à 100% en ascii et à avoir des chaînes ailleurs.

7
Lars D

En utilisant l'extension com_dotnet PHP, vous pouvez accéder au Scripting.FileSystemObject de Windows, puis faire tout ce que vous voulez avec les noms de fichiers/dossiers UTF-8.

Je l'ai emballé comme un wrapper de flux PHP, il est donc très facile à utiliser:

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

Commencez par vérifier que l'extension com_dotnet est activée dans votre php.ini Activez ensuite l'encapsuleur avec:

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

Enfin, utilisez les fonctions auxquelles vous êtes habitué (mkdir, fopen, renommer, etc.), mais préfixez votre chemin avec win://

Par exemple:

<?php
$dir_name = "Depósito";
mkdir('win://' . $dir_name );
?>
3
Nicolas Grekas

Vous pouvez utiliser cette extension pour résoudre votre problème: https://github.com/kenjiuno/php-wfio

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

Je n'ai pas besoin d'écrire beaucoup, ça marche bien:

<?php
$dir_name = mb_convert_encoding("Depósito", "ISO-8859-1", "UTF-8");
mkdir($dir_name);
?>
0
Yesterday

Essayez l'assistant de texte CodeIgniter à partir de ce lien En savoir plus sur la fonction convert_accented_characters ()

0
TomoMiha

Mon ensemble d'outils pour utiliser le système de fichiers avec UTF-8 sur Windows OU linux via PHP et compatible avec le fichier de contrôle .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);

}

Ressources supplémentaires

0
RafaSashi