glob()在Windows上找不到包含多字节字符的文件名?

Wes*_*rch 30 php windows filesystems utf-8 multibyte

我正在编写一个文件管理器,需要扫描目录并处理重命名可能有多字节字符的文件.我正在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这些文件,通过在全绝对路径(使用记事本++,PHP文件本身是UTF-8编码没有BOM).我确定路径是正确的,因为没有多字节字符的相邻文件会返回TRUE.

编辑:glob() 可以找到一个名为的文件filename-äöü.jpg.以前在我的.htaccess文件中,我有AddDefaultCharset utf-8,我之前没有考虑过.filename-äöü.jpg打印为filename-???.jpg.删除htaccess行的唯一效果似乎是文件名正常打印.

我已.htaccess完全删除了该文件,这是我的实际测试脚本(我从原始帖子中更改了几个文件名):

print_r(scandir('./uploads/')); 
print_r(glob('./uploads/*'));
Run Code Online (Sandbox Code Playgroud)

在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
)
Run Code Online (Sandbox Code Playgroud)

远程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
)
Run Code Online (Sandbox Code Playgroud)

由于这是一个不同的服务器,不管是什么平台的 - 配置可能是不同的,所以我不知道怎么想的,我不能完全用别针把它在Windows,但(可能是我的PHP安装,INI设置,或Apache的配置) .有任何想法吗?

vir*_*ior 7

看起来像glob()函数取决于你的PHP副本是如何构建的,以及它是否是用unicode感知的WIN32 API编译的(我不相信标准的构建是.

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

摘自对该文章的评论:

Philippe Verdy 2010-09-26 8:53 am

您在Windows上安装PHP的输出很容易解释:您安装了错误版本的PHP,并使用了未编译的版本来使用Win32 API的Unicode版本.因此,PHP使用的文件系统调用将使用传统的"ANSI"API,因此与此版本的PHP链接的C/C++库将首先尝试将UTF-8编码的PHP字符串转换为本地"ANSI"在运行环境中选择的代码页(从命令行窗口启动PHP之前,请参阅CHCP命令)

您的Windows版本绝对不是这个奇怪的事情的责任.实际上,这是你的PHP版本,它没有正确编译,并且使用传统的ANSI版本的Win32 API(为了与传统的16位版本的Windows 95/98兼容,其内核中的文件系统支持实际上没有直接支持Unicode,但在使用API​​的实际ANSI版本之前,使用内部转换层将Unicode转换为本地ANSI代码页.

使用编译器选项重新编译PHP以使用Win32 API的UNICODE版本(这应该是今天的默认版本,并且无论如何总是安装在永远不会是Windows 95或Windows 98的服务器上的PHP的默认值...)

然后,Windows将能够存储UTF-16编码的文件名(包括在FAT32卷上,即使在这些卷上,它也会使用文件系统的默认代码页生成8.3格式的别名短名称,这在NTFS卷中是可以避免的).

您描述的所有内容都是PHP的问题(错误地移植到Windows,或者在运行时识别不正确的系统版本):重新阅读随PHP解释编译标志的README文件.我真的认为Windows上的makefile应该能够配置和自动检测,如果它真的只需要使用API​​的ANSI版本.如果要为服务器编译它,请确保Configure脚本将有效地检测到Win32 aPI的UNICODE版本的完全支持,并在编译PHP和选择要链接的运行时库时使用它.

我在Windows上使用PHP,正确编译,我绝对不知道你在文章中引用的问题.

让我们永远忘记这些非UNICODE版本的Win32 API(它们使用不一致的Windows图形用户界面的本地ANSI代码页,以及文件系统API的OEM代码页,DOS/BIOS兼容的API,控制台API):这些非Unicode版本的API甚至比API的Unicode版本更慢且更昂贵,因为它们实际上是在使用核心Unicode API之前将代码页转换为Unicode(基于Windows NT的内核上的情况正是如此)与基于虚拟DOS扩展程序的Windows版本的情况相反,例如Windows 95/98/ME.

当您不使用API​​的原生版本时,您的API调用将通过一个thunking层,该层将在Unicode与旧的ANSI或CHCP选择的OEM代码页之一或文件系统上暗示的OEM代码页之间转换字符串. :这需要在非本机版本的Win32 API中进行额外的临时内存分配.在通过调用本机API进行实际工作之前,这需要额外的时间来转换.

总结:您在Windows上安装的PHP二进制文件必须是不同的,具体取决于您是否为Windows 95/98/SE(或者Windows 3.x的旧Win16s仿真层编译它,它对UTF-8的支持非常小,仅支持从DOS扩展程序启动Windows时选择的ANSI和OEM密码使用的Unicode Unicode子集,或者它是基于NT内核为任何其他版本的Windows编译的.

最好的证明,这是PHP而不是Windows的问题,你的奇怪结果不会出现在其他语言中,如C#,Javascript,VB,Perl,Ruby ...... PHP在跟踪版本方面有着非常糟糕的历史(也是今天应该禁用的许多历史源代码怪癖和错误的假设,以及一个不一致的库,它继承了最初在旧版本的PHP中为旧版本的Windows制作的所有怪癖,这些版本甚至不再受到微软甚至PHP的正式支持本身!).

换句话说:RTM!或者使用正确的设置下载并安装PHP for Windows precompield的二进制版本:我真的认为PHP应该分发已经为Win32 API的Unicode版本默认编译的Windows二进制文件,并使用Unicode版本的C/C++库:内部PHP代码在调用Win32 API之前将其UTF-8字符串转换为UTF-16,并在检索Win32结果时从UTF-16转换为UTF-8,而不是将PHP的内部UTF-8字符串转换回/本地OEM代码页(用于文件系统调用)或本地ANSI代码页(用于所有其他Win32 API,包括注册表或进程).