我正在编写文件管理器,需要扫描目录并处理重命名可能包含多字节字符的文件。我正在Windows / Apache PHP 5.3.8上本地工作,目录中包含以下文件名:
在实时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配置)。有什么想法吗?
答案 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
(我认为你的问题来自扫描目录,而不是来自显示代码本身或来自标题,因为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/*'));