我有一个字符串,我想用它作为文件名,所以我想用Python删除文件名中不允许的所有字符。
我宁愿严格一点,所以假设我想只保留字母、数字和一小组其他字符,如“_-.()”。”。最优雅的解决方案是什么?
文件名需要在多个操作系统(Windows, Linux和Mac OS)上有效——它是我库中的一个MP3文件,以歌曲标题为文件名,并在3台机器之间共享和备份。
我有一个字符串,我想用它作为文件名,所以我想用Python删除文件名中不允许的所有字符。
我宁愿严格一点,所以假设我想只保留字母、数字和一小组其他字符,如“_-.()”。”。最优雅的解决方案是什么?
文件名需要在多个操作系统(Windows, Linux和Mac OS)上有效——它是我库中的一个MP3文件,以歌曲标题为文件名,并在3台机器之间共享和备份。
当前回答
为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)
其他回答
仍然没有找到一个好的库来生成有效的文件名。注意,在德语、挪威语或法语等语言中,文件名中的特殊字符非常常见,完全可以接受。所以我最终有了自己的图书馆:
# util/files.py
CHAR_MAX_LEN = 31
CHAR_REPLACE = '_'
ILLEGAL_CHARS = [
'#', # pound
'%', # percent
'&', # ampersand
'{', # left curly bracket
'}', # right curly bracket
'\\', # back slash
'<', # left angle bracket
'>', # right angle bracket
'*', # asterisk
'?', # question mark
'/', # forward slash
' ', # blank spaces
'$', # dollar sign
'!', # exclamation point
"'", # single quotes
'"', # double quotes
':', # colon
'@', # at sign
'+', # plus sign
'`', # backtick
'|', # pipe
'=', # equal sign
]
def generate_filename(
name, char_replace=CHAR_REPLACE, length=CHAR_MAX_LEN,
illegal=ILLEGAL_CHARS, replace_dot=False):
''' return clean filename '''
# init
_elem = name.split('.')
extension = _elem[-1].strip()
_length = length - len(extension) - 1
label = '.'.join(_elem[:-1]).strip()[:_length]
filename = ''
# replace '.' ?
if replace_dot:
label = label.replace('.', char_replace)
# clean
for char in label + '.' + extension:
if char in illegal:
char = char_replace
filename += char
return filename
generate_虚构(“nucgae zutaaer .0.1 docx”,replace_dot=False)
nucgae_zutaäer..0.1.docx
generate_虚构(“nucgae zutaaer .0.1 docx”,replace_dot=True)
nucgae_zutaäer__0_1.docx
就像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)
更新
在这个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
我喜欢这里的python-slugify方法,但它也剥离点,这是不希望的。所以我优化了上传一个干净的文件名到s3:
pip install python-slugify
示例代码:
s = 'Very / Unsafe / file\nname hähä \n\r .txt'
clean_basename = slugify(os.path.splitext(s)[0])
clean_extension = slugify(os.path.splitext(s)[1][1:])
if clean_extension:
clean_filename = '{}.{}'.format(clean_basename, clean_extension)
elif clean_basename:
clean_filename = clean_basename
else:
clean_filename = 'none' # only unclean characters
输出:
>>> clean_filename
'very-unsafe-file-name-haha.txt'
这是如此的故障安全,它适用于没有扩展名的文件名,甚至只适用于不安全的字符文件名(这里的结果是none)。
您可以将列表推导式与字符串方法一起使用。
>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'