Подтвердить что ты не робот

Glob() не может найти имена файлов с многобайтовыми символами в Windows?

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

  • filename.jpg
  • имяфайла .jpg
  • Файл 件 name.jpg
  • פילענאַמע. JPG
  • 文件 名.jpg

Тестирование на реальном сервере UNIX прекратилось. Тестирование локально в Windows с помощью glob('./path/*') возвращает только первый, filename.jpg.

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

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

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

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

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

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

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). Любые идеи?

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

Если вы не используете родную версию API, ваш вызов API будет пройти через слой thunking, который перекодирует строки между Unicode и одна из устаревших кодовых страниц OEM или CHCP, или кодовая страница OEM намекала на файловую систему: для этого требуется дополнительная временное распределение памяти в неродной версии Win32 API. Это занимает дополнительное время, чтобы преобразовать вещи, прежде чем фактическую работу, вызывая собственный API.

Вкратце: двоичный файл PHP, который вы устанавливаете в Windows, ДОЛЖЕН быть другим в зависимости от того, если вы скомпилировали его для Windows 95/98/SE (или старого Уровень эмуляции Win16s для Windows 3.x, который был очень малым поддержка UTF-8, только для поддержки подмножеств Unicode используемого Unicode по кодам ANSI и OEM, выбранным при запуске Windows из DOS extender), или если он был скомпилирован для любой другой версии Windows на ядре NT.

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

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

Ответ 2

Я не трогал PHP уже 3 или 4 года, но, возможно, это может помочь:

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

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

pathinfo - прочитать вторую заметку

о setlocale

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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