我有一个字符串,我想用它作为文件名,所以我想用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'
其他回答
使用字符串作为文件名的原因是什么?如果人类的可读性不是一个因素,我会使用base64模块,它可以产生文件系统安全字符串。它是不可读的,但你不需要处理碰撞,而且它是可逆的。
import base64
file_name_string = base64.urlsafe_b64encode(your_string)
更新:根据Matthew的评论修改。
不完全是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+-_ .'))
结果“有些”可读,至少从系统管理员的角度来看是这样。
This whitelist approach (ie, allowing only the chars present in valid_chars) will work if there aren't limits on the formatting of the files or combination of valid chars that are illegal (like ".."), for example, what you say would allow a filename named " . txt" which I think is not valid on Windows. As this is the most simple approach I'd try to remove whitespace from the valid_chars and prepend a known valid string in case of error, any other approach will have to know about what is allowed where to cope with Windows file naming limitations and thus be a lot more complex.
>>> import string
>>> valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
>>> valid_chars
'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
>>> filename = "This Is a (valid) - filename%$&$ .txt"
>>> ''.join(c for c in filename if c in valid_chars)
'This Is a (valid) - filename .txt'
更复杂的是,仅仅删除无效字符并不能保证获得有效的文件名。由于允许的字符在不同的文件名上不同,保守的方法可能最终会将一个有效的名称变成一个无效的名称。你可能想在以下情况下添加特殊处理:
字符串都是无效字符(留下一个空字符串) 你最终会得到一个具有特殊含义的字符串,例如"."或".." 在windows中,某些设备名称是保留的。例如,你不能创建一个名为“nul”,“null .txt”(或nul.txt)的文件。)保留的名字是: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9
您可能可以通过在文件名前添加一些字符串来解决这些问题,这些字符串永远不会导致这些情况之一,并剥离无效字符。
为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)