Python 字符串去前后缀:strip 为什么会把文件名也削短
文件扩展名消失了,正文也少了一个字母
批量整理文件名时,常见需求是把末尾的点加扩展名去掉。有人把它写成 rstrip,传入点和三个字母,结果大部分文件看似正常,只有少数名称突然变短。问题出在参数的含义:这个方法接收一组可以从边缘删除的字符,每次检查一个字符,并不把整段参数当作必须完整匹配的后缀。
例如 report.txt 的末尾除了扩展名,正文最后一个字母也属于指定集合。删除会继续向左,直到遇到集合之外的字符才停。类似问题也会出现在日志前缀、业务编号和导入标签中,而且结果往往仍然是合法字符串,很难靠异常发现。
让最小样本直接显示两种规则
下面保存为 Python 文件运行,要求三点九及以上版本,本文在三点十二版本验证。示例只处理内存字符串,不改动真实文件。先比较同一个输入的两种处理结果,再验证重复前缀、空参数、大小写以及格式不完整时的行为。
清理函数 remove_wrapper 刻意要求输入同时具有固定前缀和后缀,并要求中间有内容。这是示例定义的编号格式,读者可以更换规则,但应保留“先判断格式,再取出内容”的顺序,防止把未变化的字符串误当成已经通过验证的编号。
AI概念配图,非真实界面
name = 'report.txt'
assert name.rstrip('.txt') == 'repor'
assert name.removesuffix('.txt') == 'report'
print('character set:', name.rstrip('.txt'))
print('exact suffix:', name.removesuffix('.txt'))
tagged = 'tag:tag:blue'
assert tagged.lstrip('tag:') == 'blue'
assert tagged.removeprefix('tag:') == 'tag:blue'
assert 'report.TXT'.removesuffix('.txt') == 'report.TXT'
assert 'abc'.removesuffix('') == 'abc'
assert 'abc'.removeprefix('') == 'abc'
assert ' a b '.strip() == 'a b'
print('one prefix:', tagged.removeprefix('tag:'))
def remove_wrapper(value):
prefix, suffix = 'item:', '.txt'
if not value.startswith(prefix) or not value.endswith(suffix):
raise ValueError('expected item:<name>.txt')
core = value.removeprefix(prefix).removesuffix(suffix)
if not core:
raise ValueError('name must not be empty')
return core
assert remove_wrapper('item:report.txt') == 'report'
assert remove_wrapper('item:notes.txt.txt') == 'notes.txt'
for value in ['', 'report.txt', 'item:report', 'item:.txt', 'item:report.TXT']:
try:
remove_wrapper(value)
except ValueError:
pass
else:
raise AssertionError('invalid wrapper accepted: ' + value)
print('format checks passed')精确删除一次,不代表删除所有重复项
运行后,字符集合方式得到 repor,精确方式得到 report。重复标签只去掉最前面一份,后面的标签仍然保留。这正适合表达“剥掉一层包装”。如果业务确实需要反复剥离,应单独规定允许重复多少次,并在循环中检查停止条件,不能假定精确方法会自动处理全部重复项。
精确方法在没有匹配时返回内容不变的字符串,因此它适合可有可无的修饰。它不会因为缺少必需后缀而报错,也不会自动验证扩展名是否合法。示例中的异常分支承担这部分责任,删除操作只承担取出内容的责任,两个步骤的结果才容易解释。
空白与大小写也属于输入协议
没有参数的 strip 会移除两端的空白字符,范围不限于普通空格。若输入来自固定宽度记录,或者边缘空白具有含义,就不能先习惯性调用它。示例保留字符串内部空格,是为了强调边缘清理与内容替换的范围不同;需要处理内部字符时,应另外写出明确规则。
这些方法按字符串实际内容比较,不会自动忽略大小写,也不会替你处理视觉相似字符。大写扩展名在本例中不匹配小写后缀,是可以预期的行为。若业务允许多种写法,先列出允许的形式,再决定如何生成比较值,并保留原始名称供展示与追查。
也不要用 replace 去模拟只去掉末尾一份,因为它默认处理字符串中所有匹配位置。正文里恰好出现同一段文字时,修改范围就扩大了。对于真实路径,应先分清目录与文件名,再处理目标部分;这个字符串实验本身不负责路径解析,也不判断文件是否存在。
验收要覆盖看起来不像错误的结果
最有价值的测试不是只放一个正常文件名,而是让正文末尾也包含扩展名中的字母,再加入重复前缀、完全不匹配、只有包装没有正文以及空字符串。这样可以同时检查删除规则和输入协议,发现那些没有抛异常却悄悄丢失内容的情况。
保存原始值与处理后值也很有帮助。批量清理时,可以先统计多少行发生了变化、多少行被格式检查拒绝,再抽查有代表性的样本。只有规则确认后才把结果写回业务数据,避免让字符串清理承担它无法证明的数据纠错任务。


