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

时间:2012-03-11 22:38:21

标签: php windows utf-8 filesystems multibyte

我正在编写文件管理器,需要扫描目录并处理重命名可能包含多字节字符的文件。我正在Windows / Apache PHP 5.3.8上本地工作,目录中包含以下文件名:

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

在实时UNIX服务器上进行的测试很好。使用glob('./path/*')在Windows上进行本地测试仅返回第一个filename.jpg

使用scandir(),至少返回正确数量的文件,但我得到?????????.jpg这样的名称(注意:这些是常规问号,而不是 字符。

我最终需要编写一个“搜索”功能,在整个树中递归搜索匹配模式或特定文件扩展名的文件名,我认为glob()将是正确的工具而不是扫描所有文件,并在应用程序代码中进行模式匹配和数组构建。如果需要,我愿意接受其他建议。

假设这是一个常见的问题,我立即搜索了Google和Stack Overflow,发现没有任何相关内容。这是一个Windows问题吗? PHP的缺点?解决方案是什么:我能做些什么吗?

附录:不确定这是多么相关,但file_exists()也为这些文件返回FALSE,传入完整的绝对路径(使用Notepad ++,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/*'));

在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 个答案:

答案 0 :(得分: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"代码页   在运行环境中选择(请参阅之前的CHCP命令)   从命令行窗口启动PHP)

     

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

     

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

     

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

     

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

     

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

     

让我们忘记 永远 这些非UNICODE版本的Win32   API(使用不一致的本地ANSI代码页)   Windows图形用户界面和文件系统API的OEM代码页,   DOS / BIOS兼容的API,Console API):这些非Unicode   API的版本甚至比它的速度慢且成本更高   Unicode的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,仅支持Unicode使用的Unicode子集   通过从DOS启动Windows时选择的ANSI和OEM密码   扩展器)或者是否为任何其他版本的Windows编译   在NT内核上。

     

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

     

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

答案 1 :(得分:-1)

我现在还没有触及3到4年的PHP,但也许这可能有所帮助:

  

pathinfo()可以识别语言环境,因此要正确解析包含多字节字符的路径,必须使用setlocale()函数设置匹配的语言环境

还有一些直接链接:

pathinfo - read the second note

about setlocale

(我认为你的问题来自扫描目录,而不是来自显示代码本身或来自标题,因为Chrome或firefox,如果我记得很清楚,可以处理Unicode字符。)

答案 2 :(得分:-1)

Windows上的PHP还没有使用Unicode API。因此,您必须使用运行时编码(无论它是什么)才能处理非ascii字符集。

答案 3 :(得分:-1)

从PHP 7.1开始,Windows上的long和UTF-8路径直接支持在核心。

答案 4 :(得分:-2)

在使用glob之前尝试将mb_internal_encoding()设置为“ UTF-8

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