我正在写一个文件pipe理器,需要扫描目录,并处理重命名可能有多字节字符的文件。 我正在Windows / Apache PHP 5.3.8上进行本地处理,在目录中有以下文件名:
在活的UNIX服务器上进行testing的效果很好。 在Windows上使用glob('./path/*')
在本地进行testing只返回第一个, filename.jpg
。
使用scandir()
,至less会返回正确数量的文件,但我得到的名字像?????????.jpg
(注意:那些是常规问号,而不是 字符。
我将最终需要编写一个“search”function,通过整个树search匹配模式或特定文件扩展名的文件名recursionsearch,我假设glob()
将是正确的工具,而不是扫描所有文件,并在应用程序代码中进行模式匹配和数组构build。 如果需要的话,我愿意提供替代build议。
假设这是一个常见的问题,我马上search谷歌和堆栈溢出,甚至没有发现任何相关的。 这是Windows的问题吗? PHP的缺点? 什么是解决scheme:有什么我可以做的吗?
附录:不知道这是如何相关的,但file_exists()
也返回这些文件的FALSE
,传递完整的绝对path(使用Notepad ++,PHP文件本身是UTF-8编码没有BOM)。 我确定path是正确的,因为没有多字节字符的邻居文件返回TRUE
。
编辑 : glob()
可以find一个名为filename-äöü.jpg
的文件。 以前在我的.htaccess
文件中,我有AddDefaultCharset utf-8
,这是我之前没有考虑过的。 filename-äöü.jpg
正在打印为filename- .jpg
。 除去那个htaccess行似乎有唯一的效果是现在的文件名正常打印。
我已经完全删除了.htaccess
文件,这是我完整的实际testing脚本(我从原始文章中更改了几个文件名):
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 )
由于这是一个不同的服务器,无论平台 – configuration可能不同,所以我不知道该怎么想,而且我不能完全将其固定在Windows上(可以是我的PHP安装,ini设置或Apacheconfiguration) 。 有任何想法吗?
它看起来像glob()函数取决于您的PHP副本是如何构建的,以及是否使用了一个支持unicode的WIN32 API进行编译(我不相信标准的builid是。
参看 http://www.rooftopsolutions.nl/blog/filesystem-encoding-and-php
摘自文章评论:
Philippe Verdy 2010-09-26 8:53 am
在Windows上安装PHP的输出很容易解释:您安装了错误的PHP版本,并且使用了未编译的版本来使用Unicode版本的Win32 API。 由于这个原因,PHP使用的文件系统调用将使用传统的“ANSI”API,因此与此版本的PHP链接的C / C ++库将首先尝试将UTF-8编码的PHP字符串转换为本地“ANSI”在运行环境中选择代码页(在从命令行窗口启动PHP之前,请参阅CHCP命令)
您的Windows版本最可能不负责这个奇怪的事情。 实际上,这是您的PHP版本,编译不正确,并且使用传统的ANSI版本的Win32 API(为了与Windows 95/98的旧版本的16位版本兼容,而这些版本在内核中的文件系统实际上并没有直接的支持Unicode,但在使用实际的ANSI版本的API之前,使用内部转换层将Unicode转换为本地ANSI代码页)。
使用编译器选项重新编译PHP以使用UNICODE版本的Win32 API(这应该是现在的默认值,并且始终是安装在永远不会是Windows 95或Windows 98的服务器上的默认PHP …)
然后,Windows将能够存储UTF-16编码的文件名(包括在FAT32卷上,即使在这些卷上也会使用文件系统的默认代码页以8.3格式生成别名短名,这在NTFS卷中可以避免)。
所有描述的都是PHP的问题(错误地移植到Windows,或者在运行时不正确的系统版本识别):重新阅读解释编译标志的PHP源代码的README文件。 我真的认为,Windows上的生成文件应该能够配置和自动检测,如果它真的只需要使用API的ANSI版本。 如果您正在编译服务器,请确保配置脚本将有效地检测到对Win32 aPI的UNICODE版本的全面支持,并在编译PHP和选择运行时库链接时使用它。
我在Windows上使用PHP,正确编译,我完全不知道你在文章中引用的问题。
现在让我们永远忘记Win32 API的这些非UNICODE版本(它们使用Windows图形用户界面的本地ANSI代码页,以及文件系统API的OEM代码页,DOS / BIOS兼容的API,控制台API)。这些非Unicode版本的API甚至比Unicode版本的API更慢,更昂贵,因为它们实际上是在使用核心Unicode API之前将代码页转换为Unicode(基于Windows NT的内核的情况正是从基于虚拟DOS扩展器(如Windows 95/98 / ME)的Windows版本的情况反转)。
如果您不使用API的本地版本,则您的API调用将通过thunking层,该层将对Unicode和旧版ANSI或CHCP选定的OEM代码页之一之间的字符串进行转码,或者在代码页上暗示文件系统:这需要在Win32 API的非本机版本内进行额外的临时内存分配。 这需要额外的时间来通过调用本地API来完成实际工作之前进行转换。
总之:在Windows上安装的PHP二进制文件必须有所不同,具体取决于是否针对Windows 95/98 / SE(或Windows 3.x的旧版Win16s仿真层,它具有对UTF-8的最小支持,只支持从DOS扩展器启动Windows时选择的ANSI和OEM codapges所使用的Unicode的Unicode子集),或者是针对基于NT内核的任何其他版本的Windows编译的。
最好的证明,这是一个PHP的问题,而不是Windows的问题是,你的奇怪的结果将不会发生在其他语言,如C#,Javascript,VB中,Perl中,Ruby中… PHP有一个非常糟糕的历史跟踪版本许多历史源代码的怪癖和错误的假设,应该被禁止今天,以及一个不一致的库,继承了旧版本的PHP,旧版本的Windows,甚至不再正式支持,由微软甚至PHP本身!)。
换句话说:RTM! 或者使用正确的设置下载并安装适用于Windows预压缩的PHP二进制版本:我真的认为PHP应该分发默认情况下为Win32 API的Unicode版本编译的Windows二进制文件,以及使用Unicode版本的C / C ++库:在内部调用Win32 API之前,PHP代码会将它的UTF-8字符串转换为UTF-16,并且在检索Win32结果时将其从UTF-16转换回UTF-8,而不是将PHP的内部UTF-8字符串转换回本地OEM代码页(用于文件系统调用)或本地ANSI代码页(用于所有其他Win32 API,包括注册表或进程)。
现在我还没有碰到PHP 3或4年,但也许这可能有助于:
pathinfo()是locale感知的,因此为了正确解析包含多字节字符的路径,必须使用setlocale()函数设置匹配的语言环境
还有一些直接链接:
pathinfo – 读取第二个注释
关于setlocale
(我认为你的问题来自扫描目录,而不是从显示代码本身或从标题,因为如果我记得,Chrome或Firefox,可以处理Unicode字符。)
Windows上的PHP不使用Unicode API。 所以你必须使用运行时编码(不管它是什么)来处理非ASCII字符集。
从PHP 7.1开始,Windows中的long和UTF-8路径直接支持在内核中。
尝试在使用glob之前将mb_internal_encoding()设置为“ UTF-8 ”
mb_internal_encoding("UTF-8"); print_r(glob('./uploads/*'));