Как использовать функции файловой системы в PHP, используя строки UTF-8?

Я не могу использовать mkdir для создания папок с символами UTF-8.

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

Но, когда я просматриваю эту папку в Проводнике Windows, имя папки выглядит следующим образом:

 Depósito 

Что мне делать?

Просто urlencode строку в качестве имени файла. Все символы, возвращаемые с urlencode , действительны в именах файлов (NTFS / HFS / UNIX), тогда вы можете просто urldecode имена файлов в UTF-8 (или какую бы кодировку они не были).

Предостережения (все применимы также к решениям ниже):

  • После URL-кодирования имя файла должно быть меньше 255 символов (возможно, байтов).
  • UTF-8 имеет несколько представлений для многих символов (с использованием сочетания символов). Если вы не нормализуете UTF-8, у вас могут возникнуть проблемы с поиском с помощью glob или повторным открытием отдельного файла.
  • Вы не можете полагаться на scandir или подобные функции для альфа-сортировки. Вы должны urldecode имена файлов, а затем использовать алгоритм сортировки, который знает UTF-8 (и сортировки).

Худшие решения

Ниже представлены менее привлекательные решения, более сложные и с большим количеством оговорок.

В Windows оболочка файловой системы PHP ожидает и возвращает строки ISO-8859-1 для имен файлов / каталогов. Это дает вам два варианта:

  1. Используйте UTF-8 свободно в своих именах файлов, но поймите, что символы, отличные от ASCII, будут отображаться некорректно за пределами PHP. Символ не-ASCII UTF-8 будет храниться как несколько одиночных символов ISO-8859-1. Например, в Windows Explorer будет отображаться как ó .

  2. Ограничьте имена файлов / каталогов символами, представленными в ISO-8859-1 . На практике вы передадите свои строки UTF-8 через utf8_decode перед их использованием в функциях файловой системы и передадите записи, которые scandir дает вам через utf8_encode чтобы получить исходные имена файлов в UTF-8.

Осторожно!

  • Если какой-либо байт, переданный функции файловой системы, соответствует недопустимому символу файловой системы Windows в ISO-8859-1, вам не повезло.
  • Windows может использовать кодировку, отличную от ISO-8859-1, в неанглийских локалях. Я предполагаю, что это будет обычно один из ISO-8859- #, но это означает, что вам нужно будет использовать mb_convert_encoding вместо utf8_decode .

Этот кошмар – это то, почему вы должны просто транслитерировать, чтобы создавать имена файлов.

В Unix и Linux (и, возможно, в OS X тоже), текущая кодировка файловой системы задается параметром locale LC_CTYPE (см. Функцию setlocale() ). Например, он может оценивать что-то вроде en_US.UTF-8 что означает, что кодировка UTF-8. Затем имена файлов и их пути могут быть созданы с помощью fopen() или получены с помощью dir() с помощью этой кодировки.

В Windows, PHP работает как «программа, не поддерживающая Unicode», тогда имена файлов преобразуются обратно и вперед из UTF-16, используемого файловой системой (Windows 2000 и более поздние), на выбранную «кодовую страницу». Панель управления «Параметры региона и языка» на вкладке «Форматы» устанавливает кодовую страницу, полученную с помощью опции LC_CTYPE , в то время как «Административный -> Язык для программ, не поддерживающих Юникод», устанавливает кодовую страницу перевода для имен файлов. В западных странах параметр LC_CTYPE оценивается как нечто вроде language_country.1252 где 1252 – это кодовая страница, также известная как «кодировка Windows-1252», которая аналогична (но не совсем точно) по ISO-8859-1. В Японии обычно указывается кодовая страница 932 и т. Д. Для других стран. В PHP вы можете создавать файлы, чье имя может быть выражено текущей кодовой страницей. И наоборот, имена файлов и пути, полученные из файловой системы, преобразуются из UTF-16 в байты с использованием текущей кодовой страницы наилучшего соответствия .

Это сопоставление аппроксимировано, поэтому некоторые символы могут быть искажены непредсказуемым образом. Например, Caffé Brillì.txt будет возвращен dir() как строка PHP Caff\xE9 Brill\xEC.txt как и ожидалось, если текущая кодовая страница – 1252, в то время как она вернет примерный Caffe Brilli.txt в японской системе потому что акцентированные гласные отсутствуют на кодовой странице 932, а затем заменены их «лучшими» неактивными гласными. Персонажи, которые не могут быть переведены вообще, извлекаются как ? (вопросительный знак). В общем, под Windows нет безопасного способа обнаружения таких артефактов.

Более подробные сведения доступны в моем ответе на ошибку PHP. 47096 .

Проблема в том, что Windows использует utf-16 для строк файловой системы, тогда как Linux и другие используют разные наборы символов, но часто utf-8. Вы указали строку utf-8, но это интерпретируется как еще одна 8-битная кодировка символов в Windows, возможно, Latin-1, а затем символ не-ascii, который закодирован с помощью 2 байтов в utf-8, обрабатывается как если в Windows это было 2 символа.

Нормальным решением является сохранение исходного кода на 100% в ascii и наличие строк в другом месте.

PHP 7.1 поддерживает имена файлов UTF-8 в Windows, не обращая внимания на кодовую страницу OEM.

Благодарю.

Используя расширение com_dotnet PHP, вы можете получить доступ к Windows Scripting.FileSystemObject , а затем сделать все, что хотите, с именами файлов / папок UTF-8.

Я упаковал это как оболочку потока PHP, поэтому он очень прост в использовании:

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

Сначала убедитесь, что расширение com_dotnet включено в вашем php.ini затем включите оболочку с помощью:

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

Наконец, используйте функции, к которым вы привыкли (mkdir, fopen, rename и т. Д.), Но префикс вашего пути с помощью win://

Например:

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

Вы можете использовать это расширение для решения своей проблемы: https://github.com/kenjiuno/php-wfio

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

Попробуйте использовать CodeIgniter Text helper из этой ссылки. Читайте о функции convert_accented_characters (), это может быть оценено

Мой набор инструментов для использования файловой системы с UTF-8 в Windows или Linux через PHP и совместим с файлом проверки .htaccess существует:

 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); } 

Дополнительные ресурсы