Как использовать функции файловой системы в PHP, используя строки UTF-8?
Я не могу использовать mkdir
для создания папок с символами UTF-8:
<?php
$dir_name = "Depósito";
mkdir($dir_name);
?>
когда я просматриваю эту папку в Windows Explorer, имя папки выглядит так:
Depósito
Что я должен делать?
Я использую php5
Ответы
Ответ 1
Просто urlencode
строка, требуемая как имя файла. Все символы, возвращенные из urlencode
, действительны в именах файлов (NTFS/HFS/UNIX), вы можете просто urldecode
вернуть имена файлов в UTF-8 (или какую бы кодировку они не были).
Предостережения (все применимы также к решениям ниже):
- После URL-кодирования имя файла должно быть меньше 255 символов (возможно, байтов).
- UTF-8 имеет несколько представлений для многих символов (с использованием сочетания символов). Если вы не нормализуете UTF-8, может возникнуть проблема с поиском с помощью
glob
или повторным открытием отдельного файла.
- Вы не можете полагаться на
scandir
или подобные функции для альфа-сортировки. Вы должны urldecode
указать имена файлов, используя алгоритм сортировки, который знает UTF-8 (и сортировки).
Хуже решений
Ниже представлены менее привлекательные решения, более сложные и с большим количеством оговорок.
В Windows оболочка файловой системы PHP ожидает и возвращает строки ISO-8859-1 для имен файлов/каталогов. Это дает вам два варианта:
-
Используйте UTF-8 свободно в своих именах файлов, но поймите, что символы, отличные от ASCII, будут отображаться некорректно вне PHP. Не-ASCII UTF-8 char будет сохранен как несколько одиночных символов ISO-8859-1. Например. ó
будет отображаться как ó
в проводнике Windows.
-
Ограничьте имена файлов/каталогов символы, представленные в 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
.
Этот кошмар - это то, почему вы должны просто транслитерировать для создания имен файлов.
Ответ 2
В 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.
Ответ 3
PHP 7.1 поддерживает имена файлов UTF-8 в Windows независимо от кодовой страницы OEM.
Ответ 4
Проблема в том, что Windows использует utf-16 для строк файловой системы, тогда как Linux и другие используют разные наборы символов, но часто utf-8. Вы указали строку utf-8, но это интерпретируется как еще одна 8-битная кодировка символов в Windows, возможно, Latin-1, а затем символ не-ascii, который закодирован с помощью 2 байтов в utf-8, обрабатывается как если в Windows это было 2 символа.
Нормальным решением является сохранение исходного кода на 100% в ascii и наличие строк в другом месте.
Ответ 5
Используя расширение 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 );
?>
Ответ 6
Вы можете использовать это расширение для решения своей проблемы: https://github.com/kenjiuno/php-wfio
$file = fopen("wfio://多国語.txt", "rb"); // in UTF-8
....
fclose($file);
Ответ 7
Попробуйте CodeIgniter Text helper из эту ссылку
Читайте о функции convert_accented_characters(), ее можно окупить
Ответ 8
Мой набор инструментов для использования файловой системы с 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);
}
Дополнительные ресурсы
Ответ 9
Мне не нужно много писать, это хорошо работает:
<?php
$dir_name = mb_convert_encoding("Depósito", "ISO-8859-1", "UTF-8");
mkdir($dir_name);
?>