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 и Stack Overflow и ничего не нашел. Это проблема Windows? Недостаток PHP? Каково решение: есть ли что-нибудь, что я могу сделать?

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

EDIT : glob() может найти файл с именем filename-äöü.jpg . Раньше в моем файле .htaccess меня был 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) , Есть идеи?

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

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

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

Philippe Verdy 2010-09-26 8:53 am

Вывод из вашей установки PHP в Windows легко объяснить: вы установили неправильную версию PHP и использовали версию, не скомпилированную для использования 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 перед использованием фактической версии API ANSI).

Перекомпилируйте PHP с помощью параметра компилятора для использования UNICODE-версии Win32 API (который должен быть по умолчанию сегодня, и в любом случае всегда используется по умолчанию для PHP, установленного на сервере, который НИКОГДА не будет Windows 95 или Windows 98 …)

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

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

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

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

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

В итоге: двоичный файл PHP, который вы устанавливаете в Windows, ДОЛЖЕН быть другим в зависимости от того, скомпилирован ли он для Windows 95/98 / SE (или старого уровня эмуляции Win16s для Windows 3.x, который имел очень минимальную поддержку UTF-8, только для поддержки подмножеств Unicode Юникода, используемого кодами ANSI и OEM, выбранными при запуске Windows из расширителя DOS) или если он был скомпилирован для любой другой версии 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 перед вызовом API Win32 и обратно из UTF-16 в UTF-8 при получении результатов Win32 вместо того, чтобы преобразовывать внутренние строки UTF-8 PHP обратно / в локальная кодовая страница OEM (для вызовов файловой системы) или локальная кодовая страница ANSI (для всех других API Win32, включая реестр или процесс).

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

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

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

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

О пользователе setlocale

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

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

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

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

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