Linux下PHP file_exists无法识别含&符号的文件名问题排查
问题分析与解决方案
核心原因
你遇到的问题大概率是原始A&B.txt文件名中的"&"并非标准ASCII半角符号(字节值0x26),而是以下情况之一:
- 外观相似的Unicode全角符号"&"(Unicode编码
U+FF06) - 文件名包含不可见控制字符,导致视觉上和
A&B.txt一致,但实际字节序列不匹配 - 跨系统复制文件时的编码转换异常(比如从Windows GBK环境复制到Debian UTF-8环境时的隐性编码错误)
而你通过touch diffA&B.txt创建的文件,使用的是标准半角"&",所以file_exists()能正常识别;scandir('.')能列出文件,是因为它直接读取系统返回的原始文件名字节流,不会做视觉上的字符匹配校验。
验证方法
用终端命令查看真实文件名
在Debian终端执行以下命令,查看文件名的原始转义字符:ls -b | grep "A.*B.txt"- 如果是标准半角"&",输出会显示
A&B.txt - 如果是全角"&",会显示类似
A\342\200\254B.txt的转义序列 - 如果有不可见字符,会显示对应的转义码
- 如果是标准半角"&",输出会显示
在PHP中对比字节序列
执行以下代码,对比手动输入的文件名和scandir返回的文件名的二进制内容:$files = scandir('.'); foreach ($files as $file) { if (str_contains($file, 'A') && str_contains($file, 'B.txt')) { echo "扫描到的文件名:$file\n"; echo "扫描文件名的十六进制:" . bin2hex($file) . "\n"; echo "手动输入的十六进制:" . bin2hex('A&B.txt') . "\n"; echo "file_exists检测结果:" . var_export(file_exists($file), true) . "\n\n"; } }如果两行十六进制结果不一致,说明文件名的实际字节和你手动输入的不匹配。
解决办法
重命名文件为标准格式
根据ls -b的输出结果,用终端命令重命名文件:- 如果是全角"&":
mv "A&B.txt" "A&B.txt" - 如果是带不可见字符的情况,复制
ls -b输出的转义字符串来重命名:
(替换mv "A\001&B.txt" "A&B.txt"\001为实际看到的转义码)
- 如果是全角"&":
在PHP中使用扫描到的文件名
如果不需要修改文件名,可以直接使用scandir返回的文件名字符串调用file_exists(),避免手动输入带来的字符匹配问题。
内容的提问来源于stack exchange,提问作者Rodrigo
相关产品推荐
相关产品推荐

