大家好,我是 SiKi老师。资源打包脚本只收集 PNG 图片,结果HERO2.PNG和bg.Png没有进入清单,先别急着改文件名。我会先打印每个文件名取出的后缀,再看过滤条件是不是按原样区分大小写比较。文件名里写着 PNG,不代表过滤代码认为它是.png。
本文在 Linux 上的 Python 3.12.15 中使用标准库 pathlib,另用 3.13.5 复跑同一脚本,结果一致。文件名全部自拟,只在内存中用纯路径对象计算,不扫描、不重命名真实资源目录,也没有测试 Unity、Godot 的导入设置。下面讨论的是工具端的筛选规则,不是引擎统一规定。
一 先用一组文件名复现漏选
准备几种常见写法,名称都是示例:
names=["hero.png","HERO2.PNG","bg.Png","icon.png.meta","pack.tar.gz",".png","noext","trailing.png."]print([nforninnamesifn.endswith(".png")])本地输出['hero.png', '.png']。endswith是逐字符比较,.PNG和.Png与.png不相同,所以被漏掉;反过来,名为.png的文件却被选中了。这里不是脚本没读到文件,而是筛选条件只对小写形式成立。
不要只看结果“少了两个”就去批量改名。资源名可能被场景、预制体或配置表引用,改名会把一个筛选问题扩大成引用问题。先修正规则,再决定命名规范要不要统一。
二 先取后缀,再统一大小写比较
把“取出扩展名”和“比较大小写”拆成两步:
frompathlibimportPurePosixPathdefis_png_name(name):returnPurePosixPath(name).suffix.lower()==".png"print([nforninnamesifis_png_name(n)])本地输出['hero.png', 'HERO2.PNG', 'bg.Png']。suffix返回最后一个组成部分的扩展名,HERO2.PNG得到.PNG,再转成小写就能和约定比较。原文件名没有被修改,清单里保留的仍是收到的原始名称,方便后续报告和查找。
文档里casefold()比lower()更彻底,例如德语ß用lower()不变,用casefold()会变成ss。对.png这样的纯 ASCII 后缀,两者本地结果相同;若团队的扩展名约定只允许 ASCII,我会直接写清这条约定,而不是依赖某一种大小写转换去处理所有字符。
三 后缀规则的几个边界
把每个名称取出的后缀打印出来,本地结果是:
'hero.png' '.png' ['.png'] 'HERO2.PNG' '.PNG' ['.PNG'] 'bg.Png' '.Png' ['.Png'] 'icon.png.meta' '.meta' ['.png', '.meta'] 'pack.tar.gz' '.gz' ['.tar', '.gz'] '.png' '' [] 'noext' '' [] 'trailing.png.' '' []icon.png.meta的最后一个后缀是.meta,按本文规则不会被当作图片,这通常是想要的结果;如果你的工具需要处理这类附属文件,要另立规则。pack.tar.gz说明suffix只取最后一段,复合后缀要看suffixes。以点开头的.png被视为没有后缀,而上一节的endswith会选中它,两种写法在这里给出相反的结论。末尾带点的名称同样没有后缀。
这些都不是对错问题,而是规则选择。我会把这几种名称放进回归样例,并在说明里写清它们应该被收集、跳过还是报错,避免下一个维护者换一种写法后结果悄悄变化。
四 通配符匹配的大小写跟随平台
有人会改用match("*.png"),但它的大小写规则默认跟随路径风格:
frompathlibimportPurePosixPath,PureWindowsPathprint(PurePosixPath("HERO2.PNG").match("*.png"))print(PureWindowsPath("HERO2.PNG").match("*.png"))print(PurePosixPath("HERO2.PNG").match("*.png",case_sensitive=False))本地依次输出False、True、True。同一段脚本在 Windows 路径规则下能选中,在 POSIX 规则下会漏掉,这就是“我电脑上好好的,构建机上少了图”的一种来源。case_sensitive参数是 3.12 新增的,旧版本不能使用。
本文用纯路径对象模拟两种规则,没有在真实 Windows 或 macOS 文件系统上扫描目录,也没有测试glob在不同文件系统上的表现。若筛选依赖实际目录遍历,还需要在交付平台上单独验证。
五 扩展名不等于文件格式
即使is_png_name("HERO2.PNG")返回True,也只能说明名字符合约定。独立实测里我放了一段内容为b"not an image"的自拟数据,它的名字通过了后缀检查,但开头并不是 PNG 文件签名。扩展名过滤只负责“挑哪些名字”,不负责“内容是不是有效图片”,本文没有实现或验证图片格式检查。
本次独立验证覆盖大小写漏选、点开头文件、复合后缀、附属文件、无后缀、末尾带点、两种路径规则、case_sensitive、大小写转换差异、名字与内容不一致以及输入列表未改变。代码只在内存中运行,不读写真实资源。它和之前讲的资源名大小写碰撞是两个问题:那篇检查两个名字在不区分大小写的系统上会不会撞车,这篇检查筛选条件有没有漏掉文件。
你遇到的是大写后缀被漏掉,还是不同电脑上选出的数量不一样?给出几条自拟文件名和你的过滤条件,就能开始排查,不需要上传项目资源。
依据:Python 3.12 pathlib 官方文档中 suffix、suffixes、match 的说明,以及 str.lower 与 str.casefold 官方文档。示例和表达原创,不引用具体课程信息。