Как использовать функции файловой системы в 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);
?>