我有一个字符串,我想用它作为文件名,所以我想用Python删除文件名中不允许的所有字符。

我宁愿严格一点,所以假设我想只保留字母、数字和一小组其他字符,如“_-.()”。”。最优雅的解决方案是什么?

文件名需要在多个操作系统(Windows, Linux和Mac OS)上有效——它是我库中的一个MP3文件,以歌曲标题为文件名,并在3台机器之间共享和备份。


当前回答

不完全是OP要求的,但这是我使用的,因为我需要唯一的和可逆的转换:

# p3 code
def safePath (url):
    return ''.join(map(lambda ch: chr(ch) if ch in safePath.chars else '%%%02x' % ch, url.encode('utf-8')))
safePath.chars = set(map(lambda x: ord(x), '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-_ .'))

结果“有些”可读,至少从系统管理员的角度来看是这样。

其他回答

>>> import string
>>> safechars = bytearray(('_-.()' + string.digits + string.ascii_letters).encode())
>>> allchars = bytearray(range(0x100))
>>> deletechars = bytearray(set(allchars) - set(safechars))
>>> filename = u'#ab\xa0c.$%.txt'
>>> safe_filename = filename.encode('ascii', 'ignore').translate(None, deletechars).decode()
>>> safe_filename
'abc..txt'

它不处理空字符串,特殊文件名('nul', 'con'等)。

为python 3.6修改的答案

import string
import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(chr(c) for c in cleanedFilename if chr(c) in validFilenameChars)

更新

在这个6年的答案中,所有的链接都无法修复。

同样,我也不会再这样做了,只是base64编码或删除不安全的字符。Python 3示例:

import re
t = re.compile("[a-zA-Z0-9.,_-]")
unsafe = "abc∂éåß®∆˚˙©¬ñ√ƒµ©∆∫ø"
safe = [ch for ch in unsafe if t.match(ch)]
# => 'abc'

使用base64可以进行编码和解码,因此可以再次检索原始文件名。

但是根据用例,最好生成一个随机文件名并将元数据存储在单独的文件或DB中。

from random import choice
from string import ascii_lowercase, ascii_uppercase, digits
allowed_chr = ascii_lowercase + ascii_uppercase + digits

safe = ''.join([choice(allowed_chr) for _ in range(16)])
# => 'CYQ4JDKE9JfcRzAZ'

原文链接:

bobcat项目包含了一个python模块来完成这个任务。

它不是完全健壮的,看看这篇文章和这个回复。

因此,如前所述:如果可读性不重要,base64编码可能是一个更好的主意。

文档https://svn.origo.ethz.ch/bobcat/src-doc/safefilename-module.html 源https://svn.origo.ethz.ch/bobcat/trunk/src/bobcatlib/safefilename.py

就像S.Lott回答的那样,你可以看看Django框架如何将字符串转换为有效的文件名。

最新和更新的版本在utils/text.py中,并定义了"get_valid_filename",如下所示:

def get_valid_filename(s):
    s = str(s).strip().replace(' ', '_')
    return re.sub(r'(?u)[^-\w.]', '', s)

(见https://github.com/django/django/blob/master/django/utils/text.py)

我相信这不是一个很好的答案,因为它修改了它循环的字符串,但它似乎工作得很好:

import string
for chr in your_string:
 if chr == ' ':
   your_string = your_string.replace(' ', '_')
 elif chr not in string.ascii_letters or chr not in string.digits:
    your_string = your_string.replace(chr, '')