Создать (нормальное/безопасное) имя файла из любой (небезопасной) строки
Я хочу создать нормальное/безопасное имя файла (т.е. несколько читаемый, никаких "странных" символов и т.д.) из некоторой случайной строки Unicode (mich может содержать только что-либо).
(Для меня не имеет значения, что функция Cocoa, ObjC, Python и т.д.)
Конечно, может быть бесконечное множество символов, которые могут быть странными. Таким образом, на самом деле это не решение иметь черный список и добавлять к нему все больше и больше.
У меня мог бы быть белый список. Однако я не знаю, как это определить. [a-zA-Z0-9 .]
- это начало, но я также хочу принимать символы Unicode, которые могут отображаться обычным способом.
Ответы
Ответ 1
Python:
"".join([c for c in filename if c.isalpha() or c.isdigit() or c==' ']).rstrip()
это принимает символы Unicode, но удаляет разрывы строк и т.д.
Пример:
filename = u"ad\nbla'{-+\)(ç?"
дает: adblaç
изменить
str.isalnum() делает буквенно-цифровой на один шаг. - комментарий от queueoverflow ниже. danodonovan намекнул на сохранение точки.
keepcharacters = (' ','.','_')
"".join(c for c in filename if c.isalnum() or c in keepcharacters).rstrip()
Ответ 2
Мои требования были консервативными (сгенерированные имена файлов должны были быть действительными для нескольких операционных систем, включая некоторые старые мобильные ОС). Я закончил с:
"".join([c for c in text if re.match(r'\w', c)])
В белом списке перечислены буквенно-цифровые символы (a-z, A-Z, 0-9) и подчеркивание. Регулярное выражение может быть скомпилировано и кэшировано для эффективности, если есть много строк для сопоставления. Для моего случая это не имело бы существенной разницы.
Ответ 3
Здесь есть несколько разумных ответов, но в моем случае я хочу взять нечто, представляющее собой строку, которая может иметь пробелы и знаки препинания, и вместо того, чтобы просто удалить их, я бы предпочел заменить это подчеркиванием. Хотя пробелы являются допустимым символом имени файла в большинстве ОС, они проблематичны. Кроме того, в моем случае, если исходная строка содержала точку, которую я не хотел, чтобы это проходило через имя файла, или это генерировало бы "дополнительные расширения", которые я мог бы не хотеть (я сам добавляю расширение)
def make_safe_filename(s):
def safe_char(c):
if c.isalnum():
return c
else:
return "_"
return "".join(safe_char(c) for c in s).rstrip("_")
print(make_safe_filename( "hello you crazy $#^#& 2579 people!!! : die!!!" ) + ".gif")
принтами:
hello_you_crazy_______2579_people______die___.gif
Ответ 4
Более или менее то, что было упомянуто здесь с помощью регулярного выражения, но в обратном порядке (замените все НЕ перечисленные):
>>> import re
>>> filename = u"ad\nbla'{-+\)(ç1?"
>>> re.sub(r'[^\w\d-]','_',filename)
u'ad_bla__-_____1_'
Ответ 5
Здесь нет решений, только проблемы, которые вы должны учитывать:
-
Какова минимальная максимальная длина имени файла? (например, DOS поддерживает только 8-11 символов, большинство ОС не поддерживают > 256 символов)
-
какие имена файлов запрещены в каком-то контексте? (Windows все еще не поддерживает сохранение файла как CON.TXT
- см. https://blogs.msdn.microsoft.com/oldnewthing/20031022-00/?p=42073)
-
помните, что .
и ..
имеют определенные значения (текущий/родительский каталог) и поэтому небезопасны.
-
существует ли риск, что имена файлов будут сталкиваться - либо из-за удаления символов, либо с тем же именем, которое используется несколько раз?
Рассмотрим только хэширование данных и использование hexdump этого имени файла?
Ответ 6
Python:
for c in r'[]/\;,><&*:%[email protected]!#^()|?^':
filename = filename.replace(c,'')
(просто пример символов, которые вы хотите удалить)
r
перед строкой убеждается, что строка интерпретируется в необработанном формате, что позволяет также удалять обратную косую черту \
Изменить:
regex решение в Python:
import re
re.sub(r'[]/\;,><&*:%[email protected]!#^()|?^', '', filename)
Ответ 7
Вот то, с чем я пришел, вдохновленный uglycoyote:
import time
def make_safe_filename(s):
def safe_char(c):
if c.isalnum() or c=='.':
return c
else:
return "_"
safe = ""
last_safe=False
for c in s:
if len(safe) > 200:
return safe + "_" + str(time.time_ns() // 1000000)
safe_c = safe_char(c)
curr_safe = c != safe_c
if not last_safe or not curr_safe:
safe += safe_c
last_safe=curr_safe
return safe
И для проверки:
print(make_safe_filename( "hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!hello you crazy $#^#& 2579 people!!! : hi!!!" ) + ".gif")