我有一个字符串,我想用它作为文件名,所以我想用Python删除文件名中不允许的所有字符。
我宁愿严格一点,所以假设我想只保留字母、数字和一小组其他字符,如“_-.()”。”。最优雅的解决方案是什么?
文件名需要在多个操作系统(Windows, Linux和Mac OS)上有效——它是我库中的一个MP3文件,以歌曲标题为文件名,并在3台机器之间共享和备份。
我有一个字符串,我想用它作为文件名,所以我想用Python删除文件名中不允许的所有字符。
我宁愿严格一点,所以假设我想只保留字母、数字和一小组其他字符,如“_-.()”。”。最优雅的解决方案是什么?
文件名需要在多个操作系统(Windows, Linux和Mac OS)上有效——它是我库中的一个MP3文件,以歌曲标题为文件名,并在3台机器之间共享和备份。
当前回答
这是Windows特定路径的另一个答案,使用简单的替换,没有时髦的模块:
import re
def check_for_illegal_char(input_str):
# remove illegal characters for Windows file names/paths
# (illegal filenames are a superset (41) of the illegal path names (36))
# this is according to windows blacklist obtained with Powershell
# from: https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names/44750843#44750843
#
# PS> $enc = [system.Text.Encoding]::UTF8
# PS> $FileNameInvalidChars = [System.IO.Path]::GetInvalidFileNameChars()
# PS> $FileNameInvalidChars | foreach { $enc.GetBytes($_) } | Out-File -FilePath InvalidFileCharCodes.txt
illegal = '\u0022\u003c\u003e\u007c\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008' + \
'\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015' + \
'\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\u003a\u002a\u003f\u005c\u002f'
output_str, _ = re.subn('['+illegal+']','_', input_str)
output_str = output_str.replace('\\','_') # backslash cannot be handled by regex
output_str = output_str.replace('..','_') # double dots are illegal too, or at least a bad idea
output_str = output_str[:-1] if output_str[-1] == '.' else output_str # can't have end of line '.'
if output_str != input_str:
print(f"The name '{input_str}' had invalid characters, "
f"name was modified to '{output_str}'")
return output_str
当测试check_for_illegal_char('fas\u0003\u0004good\\..asd.'),我得到:
The name 'fas♥♦good\..asd.' had invalid characters, name was modified to 'fas__good__asd'
其他回答
请记住,在Unix系统上实际上没有文件名限制
它可能不包含\0 它可能不包含/
其他一切都是公平的。
$ touch " > even multiline > haha > ^[[31m red ^[[0m > evil" $ ls -la -rw-r--r-- 0 Nov 17 23:39 ?even multiline?haha??[31m red ?[0m?evil $ ls -lab -rw-r--r-- 0 Nov 17 23:39 \neven\ multiline\nhaha\n\033[31m\ red\ \033[0m\nevil $ perl -e 'for my $i ( glob(q{./*even*}) ){ print $i; } ' ./ even multiline haha red evil
是的,我只是将ANSI颜色代码存储在一个文件名中,并使它们生效。
为了娱乐,在目录名中放入一个BEL字符,并观看当您CD到其中时所产生的乐趣;)
我相信这不是一个很好的答案,因为它修改了它循环的字符串,但它似乎工作得很好:
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, '')
>>> 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'等)。
其他注释还没有解决的另一个问题是空字符串,这显然不是一个有效的文件名。你也可能因为剥离太多字符而得到一个空字符串。
对于Windows保留文件名和点的问题,最安全的回答是“如何从任意用户输入规范化有效文件名?”就是“连试都不用试”:如果你能找到任何其他方法来避免它(例如。使用来自数据库的整数主键作为文件名),这样做。
如果你必须,你真的需要允许空格和。’将文件扩展名作为名称的一部分,可以这样尝试:
import re
badchars= re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
badnames= re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')
def makeName(s):
name= badchars.sub('_', s)
if badnames.match(name):
name= '_'+name
return name
即使这样也不能保证正确,特别是在意想不到的操作系统上-例如RISC操作系统讨厌空格并使用'。'作为目录分隔符。
当遇到同样的问题时,我使用python-slugify。
Shoham也建议使用这种方法,但正如therealmarv指出的那样,默认情况下python-slugify也会转换圆点。
可以通过在regex_pattern参数中包含点来否决这种行为。
> filename = "This is a väryì' Strange File-Nömé.jpeg"
> pattern = re.compile(r'[^-a-zA-Z0-9.]+')
> slugify(filename,regex_pattern=pattern)
'this-is-a-varyi-strange-file-nome.jpeg'
方法复制的正则表达式模式
ALLOWED_CHARS_PATTERN_WITH_UPPERCASE
python-slugify包的slugify.py文件中的全局变量,并扩展为“。”
请记住,像.()这样的特殊字符必须用\转义。
如果您想保留大写字母,请使用小写=False参数。
> filename = "This is a väryì' Strange File-Nömé.jpeg"
> pattern = re.compile(r'[^-a-zA-Z0-9.]+')
> slugify(filename,regex_pattern=pattern, lowercase=False)
'This-is-a-varyi-Strange-File-Nome.jpeg'
这是使用Python 3.8.4和Python -slugify 4.0.1实现的