DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Python rstrip 去扩展名时误删文件名末尾字符

rstrip 按字符集合删除而不是按后缀匹配;改用 removesuffix 或受条件保护的切片,并区分任意扩展名与固定后缀。

处理 CSV 文件名时,name.rstrip('.csv') 看起来能去掉扩展名,但 results.csv 变成了 result,文件主体最后的 s 也丢了。换几个文件名又正常,容易误以为输入文件有隐藏字符。

rstrip 接收的是一组可删除字符,不是完整后缀。它会从右侧持续删除属于 .csv 这四个字符集合的字符,直到遇到集合外的字符才停止。删除 .csv 后,若主体还以 s、c、v 或句点结尾,也会继续删除。

只删除一次指定后缀

在 Python 3.9 及以后,用 removesuffix 表达“仅在末尾存在这个完整后缀时删除一次”:

names = [
    "results.csv",
    "report.v2.csv",
    "backup.csv.csv",
    "notes.txt",
]
base_names = [name.removesuffix(".csv") for name in names]
print(base_names)

按规则,结果应为:

['results', 'report.v2', 'backup.csv', 'notes.txt']

backup.csv.csv 只删除最后一次 .csv;不以 .csv 结尾的名称保持原样。这个示例只计算字符串,不会重命名或修改任何文件。

匹配区分大小写,report.CSV 不会被 .removesuffix('.csv') 修改。如果业务允许几种大小写扩展名,应另外制定匹配规则,不能为了后缀判断把整个文件名统一转小写;在区分大小写的文件系统中,那会改变路径含义。

旧版 Python 先判断,再切片

维护 Python 3.8 或更早代码时,不能直接添加 removesuffix。可以保留等价的条件判断:

def remove_suffix(text, suffix):
    if suffix and text.endswith(suffix):
        return text[:-len(suffix)]
    return text

base_name = remove_suffix("results.csv", ".csv")

这里的 suffix 非空检查有实际作用:空后缀的长度是零,text[:-0] 等同于 text[:0],会返回空字符串。函数在后缀为空时保留原值,与删除空后缀的目标一致。

不要不加判断地切掉最后四个字符;不是 CSV 的输入也可能被截断。也不要用 replace('.csv', ''),因为它默认删除所有位置上的匹配,文件主体中的 .csv 也会被移除。

去掉任意最后扩展名是另一种规则

如果需求确实是移除最后一个扩展名,而不限于 .csv,可以用路径接口:

import os.path

root, extension = os.path.splitext("report.v2.csv")
print(root, extension)

预期分别为 report.v2 和 .csv。这个接口保留主体中较早的句点;archive.tar.gz 的最后扩展名是 .gz,不是整个 .tar.gz。

点开头的文件还有自己的规则:例如 .env 通常会被当作没有扩展名的名称,不会被拆成空主体与 .env 扩展名。因此,splitext 与删除固定字符串后缀不能无条件互换。

name.split('.')[0] 也不适合代替这两种规则。它会在第一个句点截断,report.v2.csv 就只剩 report。当输入包含目录路径时,目录名中的句点还会进一步干扰这种写法。

在执行文件操作前检查名称映射

至少对照主体以 s、c、v 结尾、主体含句点、重复后缀、无后缀、不同大小写后缀等输入。检查完整的旧名称到新名称映射,而不是只看数量或抽一个正常文件。

如果计算结果会被用于批量重命名,还需要单独检查目标重名与已有文件冲突。字符串处理正确并不代表后续文件操作安全;先保存映射和必要备份,再执行明确范围内的变更。

回退这次字符串改动不会自动把已经重命名的文件改回来。保留原名称映射,才能按实际完成的操作恢复;不要再用另一轮猜测性的后缀处理反推原文件名。