glob () не удается найти имена файлов с многобайтовыми символами в Windows?

я пишу файловый менеджер и должен сканировать каталоги и заниматься переименованием файлов, которые могут иметь многобайтовые символы. Я работаю над ним локально на Windows / Apache PHP 5.3.8, со следующими именами файлов в каталоге:

  • имя файла.формат JPG
  • имяфайла.формат JPG
  • file件name.формат JPG
  • פילענאַמע.формат JPG
  • 文件名.формат JPG

тестирование на живом сервере UNIX вызвало штраф. Локальное тестирование в Windows используя glob('./path/*') возвращает только первый из них, filename.jpg.

используя scandir() правильное количество файлов возвращается, по крайней мере, но я получаю имена, как ?????????.jpg (Примечание: это обычные вопросительные знаки, а не символ�.

мне понадобится написать функцию "поиск", чтобы рекурсивно искать по всему дереву имена файлов, соответствующие шаблону или с определенным расширением файла, и я предположил glob() был бы правильным инструментом для этого, а не сканировать все файлы и выполните сопоставление шаблонов и построение массива в коде приложения. Я открыт для альтернативных предложений, если нужно.

предполагая, что это обычная проблема, я сразу же искал Google и Stack Overflow и не нашел ничего даже связанного. Это проблема Windows? РНР недостаток? Каково решение: есть ли что-нибудь, что я могу сделать?

добавление: не уверен, насколько это связано, но file_exists() возвращается FALSE для этих файлов, проходя по полному абсолютному пути (используя Notepad++, сам php-файл является UTF-8, кодирующим no BOM). Я уверен, что путь правильный, так как соседние файлы без многобайтовых символов возвращают TRUE.

редактировать: glob() можете найдите файл с именем filename-äöü.jpg. Ранее в моей , Я AddDefaultCharset utf-8, который я не учел раньше. filename-äöü.jpg печатался как filename-���.jpg. Единственным эффектом удаления этой строки htaccess был теперь этот файл имя печатается нормально.

я удалил .htaccess файл полностью, и это мой фактический тестовый скрипт в полном объеме (я изменил пару имен файлов из исходного сообщения):

print_r(scandir('./uploads/')); 
print_r(glob('./uploads/*'));

вывод локально на Windows:

Array
(
    [0] => .
    [1] => ..
    [2] => ??? ?????.jpg
    [3] => ???.jpg
    [4] => ?????????.jpg
    [5] => filename-äöü.jpg
    [6] => filename.jpg
    [7] => test?test.jpg
)
Array
(
    [0] => ./uploads/filename-äöü.jpg
    [1] => ./uploads/filename.jpg
)

вывод на удаленный сервер UNIX:

Array
(
    [0] => .
    [1] => ..
    [2] => filename-äöü.jpg
    [3] => filename.jpg
    [4] => test이test.jpg
    [5] => имя файла.jpg
    [6] => פילענאַמע.jpg
    [7] => 文件名.jpg
)
Array
(
    [0] => ./uploads/filename-äöü.jpg
    [1] => ./uploads/filename.jpg
    [2] => ./uploads/test이test.jpg
    [3] => ./uploads/имя файла.jpg
    [4] => ./uploads/פילענאַמע.jpg
    [5] => ./uploads/文件名.jpg
)

поскольку это другой сервер, независимо от конфигурации платформы может отличаться, поэтому я не уверен, что думать, и я не могу полностью закрепить его на Windows тем не менее (может быть моя установка PHP, настройки ini или конфигурация Apache). Есть идеи?

5 ответов


похоже, что функция glob () зависит от того, как была построена ваша копия PHP и была ли она скомпилирована с помощью Unicode WIN32 API (я не считаю, что стандартный builid.

Cf. http://www.rooftopsolutions.nl/blog/filesystem-encoding-and-php

выдержка из комментариев к статье:

Филипп Верди 2010-09-26 8:53 ам

вывод из вашей установки PHP В Windows легко объяснить : вы установили неправильную версию PHP и использовали версию not скомпилирован для использования Unicode-версии Win32 API. По этой причине, вызовы файловой системы, используемые PHP, будут использовать устаревший API" ANSI " и т. д библиотеки C / C++, связанные с этой версией PHP, сначала попытаются преобразование строки PHP в кодировке UTF-8 в локальную кодовую страницу ANSI выбранный в рабочей среде (см. команду CHCP перед запуск PHP из командной строки)

ваша версия Windows, скорее всего, не несет ответственности за это странно вещь. На самом деле, это ваша версия PHP, которая не компилируется правильно, и это использует устаревшую версию ANSI Win32 API (для совместимость с устаревшими 16-разрядными версиями Windows 95/98, поддержка файловой системы в ядре фактически не имела прямой поддержки Unicode, но использовал внутренний слой преобразования для преобразования Unicode в локальная кодовая страница ANSI перед использованием фактической версии ANSI этот ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.)

перекомпилировать PHP с помощью опции компилятора для использования версии UNICODE Win32 API (который должен быть по умолчанию сегодня, и в любом случае всегда значение по умолчанию для PHP, установленного на сервере, который никогда не будет Windows 95 или Windows 98...)

тогда Windows сможет хранить имена файлов в кодировке UTF-16 (включая на томах FAT32, даже если на этих томах он также будет генерировать псевдоним короткое имя в формате 8.3 с использованием файловой системы по умолчанию кодовая страница, чего можно избежать в томах NTFS).

все, что вы описываете, это проблемы PHP (неправильный перенос на Windows или неправильная идентификация версии системы во время выполнения) : перечитайте README файлах PHP кода, объясняя флаги компиляции. Я действительно думаю, что makefile в Windows должен можно настроить и автоопределение, если действительно необходимо использовать только ANSI версия API. Если вы компилируете его на сервер конечно что сценарий Configure эффективно обнаружит полное поддержка UNICODE версии Win32 aPI и будет использовать его, когда компиляция PHP и при выборе среды выполнения библиотек.

Я использую PHP В Windows, правильно скомпилирован, и я абсолютно не знаю проблемы, которые вы приводите в своей статье.

давайте забудем сейчас навсегда эти версии Win32, отличные от UNICODE API (которые используют непоследовательно локальный Кодовая страница ANSI для Графический интерфейс Windows и кодовая страница OEM для API файловой системы, DOS / BIOS-совместимые API, консольные API): эти не-Unicode версии API даже намного медленнее и дороже, чем Unicode версии API, потому что они фактически переводят кодовая страница в Unicode перед использованием основных API Unicode ( ситуация на ядрах под управлением Windows NT прямо противоположна ситуация с версиями Windows на основе виртуальной DOS экстендер, такой как Windows 95/98 / ME).

когда вы не используете собственную версию API, ваш вызов API будет пройдите через слой thunking, который перекодирует строки между Unicode и один из унаследованных ANSI или chcp-выбранных OEM-кодовых страниц, или кодовая страница OEM намекнула на файловую систему: это требует дополнительных временное выделение памяти в неродной версии Win32 ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС. Это занимает дополнительное время, чтобы преобразовать вещи, прежде чем делать фактическая работа вызывая собственный API.

in summary: двоичный файл PHP, который вы устанавливаете в Windows, должен быть другим в зависимости от того, скомпилировали ли вы его для Windows 95/98 / SE (или старого Уровень эмуляции Win16s для Windows 3.x, который имел очень mimimum поддержка UTF-8, только для поддержки Unicode подмножества Unicode используется по стандарту ANSI и OEM codapges выбранного при запуске Windows от DoS extender) или если он был скомпилирован для любой другой версии Windows на ядре NT.

лучшим доказательством того, что это проблема PHP, а не Windows, является то, что ваши странные результаты не будут происходить на других языках, таких как C#, Javascript, VB, Perl, Ruby... PHP имеет очень плохую историю отслеживания версии (и слишком много исторических причуд и ошибок исходного кода предположения, которые должны быть отключены сегодня, и непоследовательная библиотека который унаследовал все эти причуды, первоначально сделанные в старых версиях PHP для старых версий Windows, которые даже не официально поддерживается Microsoft или даже самим PHP !).

другими словами: RTM ! Или загрузите и установите двоичную версию PHP для Windows precompield с правильными настройками : я действительно думаю что PHP должен распространять двоичные файлы Windows, уже скомпилированные значение по умолчанию для Unicode-версии Win32 API и использование Unicode-версия библиотек C / C++: внутри PHP-код будет преобразуйте строки UTF-8 в UTF-16 перед вызовом Win32 API и позади UTF-16 для UTF-8 при получении результатов Win32 вместо преобразование внутренней в PHP UTF-8 строки обратно в OEM-кодировку (для вызовов файловой системы) или локальной кодовой страницы ANSI (для всех остальных Win32 API, включая реестр или процесс).


Я не касался PHP в течение 3 или 4 лет, но, возможно, это может помочь:

pathinfo() знает язык, поэтому для правильного анализа пути, содержащего многобайтовые символы, соответствующий язык должен быть установлен с помощью функции setlocale ()

и некоторые прямые ссылки:

pathinfo-прочитайте второе примечание

о setlocale

(Я думаю, что ваша проблема исходит от сканирования каталоги, а не из кода дисплея или из заголовков, так как Chrome или firefox, если я хорошо помню, могут обрабатывать символы Unicode.)


PHP в windows еще не использует API Unicode. Таким образом, вы должны использовать кодировку времени выполнения (что бы это ни было), чтобы иметь дело с не ascii-кодировкой.


начиная с PHP 7.1 long и UTF-8 пути на Windows поддерживаются непосредственно в ядре.


попробуйте mb_internal_encoding () на "UTF-8 " перед использованием glob

mb_internal_encoding("UTF-8");
print_r(glob('./uploads/*'));