Превратить строку в допустимое имя файла?

269
задан martineau 27 November 2016 в 16:18
поделиться

13 ответов

Можно посмотреть платформа Django для того, как они создают "краткий заголовок" из произвольного текста. Кратким заголовком является URL - и имя файла - дружественный.

текст Django utils определяет функцию, slugify() , это - вероятно, золотой стандарт для такого рода вещи. По существу их код следующий.

def slugify(value):
    """
    Normalizes string, converts to lowercase, removes non-alpha characters,
    and converts spaces to hyphens.
    """
    import unicodedata
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
    value = unicode(re.sub('[-\s]+', '-', value))

существует больше, но я пропустил его, так как это не обращается к slugification, но выходу.

150
ответ дан Brad Solomon 23 November 2019 в 02:19
поделиться

Этот подход белого списка (т.е., позволяя только символы, существующие в valid_chars), будет работать, если не будет пределов на форматирование файлов или комбинацию допустимых символов, которые недопустимы (как ".."), например, то, что Вы говорите, позволило бы названное имя файла ". txt", который я думаю, не допустим в Windows. Поскольку это - самый простой подход, я попытался бы удалить пробел из valid_chars и предварительно ожидать известную допустимую строку в случае ошибки, любой другой подход должен будет знать о том, что позволяется, где справиться с ограничениями именования файла Windows и таким образом быть намного более сложным.

>>> 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'
98
ответ дан Community 23 November 2019 в 02:19
поделиться

Другой проблемой, которую другие комментарии не решили еще, является пустая строка, которая является, очевидно, не допустимым именем файла. Можно также закончить с пустой строкой от разделения слишком многих символов.

, Что с Windows зарезервировало имена файлов и проблемы с точками, самым безопасным ответом на вопрос “how, я нормализую допустимое имя файла от произвольного ввода данных пользователем? ” является “don't, даже беспокоят try”: если можно найти какой-либо другой способ избежать его (например, использование целочисленных первичных ключей от базы данных как имена файлов), сделайте это.

, Если Вы должны, и действительно необходимо позволить пробелы и †˜. ’ для расширений файла как часть имени, попробуйте что-то как:

import re
badchars= re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^ 

Даже этому нельзя гарантировать право особенно на неожиданном OSs —  for пример RISC, ОС ненавидит пробелы и использует †˜. ’ как разделитель каталога.

) 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

Даже этому нельзя гарантировать право особенно на неожиданном OSs —  for пример RISC, ОС ненавидит пробелы и использует †˜. ’ как разделитель каталога.

5
ответ дан bobince 23 November 2019 в 02:19
поделиться

Почему не только обертывают "osopen" с попыткой/кроме и позволяют базовой ОС разобраться, допустим ли файл?

Это походит на намного меньшее количество работы и допустимо, неважно, который ОС Вы используете.

6
ответ дан dplante 23 November 2019 в 02:19
поделиться
>>> 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', 'довод "против"', и т.д.).

7
ответ дан jfs 23 November 2019 в 02:19
поделиться

Хотя необходимо быть осторожными. Ясно не сказано в Вашем введении, если Вы только смотрите на latine язык. Некоторые слова могут стать бессмысленными или другое значение при очистке их с символами ASCII только.

предполагают, что Вы имеете "forГЄt poГ©sie" (лесная поэзия), Ваша санитизация могла бы дать "форт-posie" (сильный + что-то бессмысленное)

Хуже, если необходимо иметь дело с китайскими символами.

"дё ‹еЊ — жІў" Ваша система мог бы закончить тем, что делал "---", который обречен перестать работать через некоторое время и не очень полезный. Таким образом, если бы Вы имеете дело только с файлами, я поощрил бы для или вызова их универсальной цепочкой, которой Вы управляете или сохранять символы как есть Для URIs о том же.

6
ответ дан karlcow 23 November 2019 в 02:19
поделиться

Следует иметь в виду, нет на самом деле никаких ограничений на имена файлов в системах 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 в него;)

13
ответ дан Kent Fredric 23 November 2019 в 02:19
поделиться

Только для дальнейшего усложнения вещей Вы, как гарантируют, не получите допустимое имя файла только путем удаления недопустимых символов. Так как допустимые символы расходятся в различных именах файлов, консервативный подход мог закончить тем, что превратил допустимое имя в недопустимое. Можно хотеть добавить специальную обработку для случаев где:

  • строка является всеми недопустимыми символами (оставляющий Вас с пустой строкой)

  • , Вы заканчиваете со строкой с особым значением, например,"." или ".."

  • окна On, определенные имена устройств резервируются. Например, Вы не можете создать файл, названный "nul", "nul.txt" (или nul.anything на самом деле), зарезервированные имена:

    ДОВОД "ПРОТИВ", PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 и LPT9

можно, вероятно, работать вокруг этих проблем путем предварительного ожидания некоторой строки к именам файлов, которые никогда не могут приводить к одному из этих случаев, и разделяющий недопустимые символы.

38
ответ дан Brian 23 November 2019 в 02:19
поделиться

Что причина состоит в том, чтобы использовать строки в качестве имен файлов? Если бы человеческая удобочитаемость не является фактором, я пошел бы с base64 модулем, который может произвести файловую систему безопасные строки. Это не будет читаемо, но Вы не должны будете иметь дело с коллизиями, и это обратимо.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

Обновление : Измененный на основе комментария Matthew.

92
ответ дан Igal Serban 23 November 2019 в 02:19
поделиться

Можно использовать понимание списка вместе со строковыми методами.

>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'
95
ответ дан John Mee 23 November 2019 в 02:19
поделиться

Вы могли использовать re.sub () метод для замены чего-либо не "подобного файлу". Но в действительности, каждый символ мог быть допустимым; таким образом, нет никаких предварительно созданных функций (я верю), чтобы сделать его.

import re

str = "File!name?.txt"
f = open(os.path.join("/tmp", re.sub('[^-a-zA-Z0-9_.() ]+', '', str))

привел бы к дескриптору файла к /tmp/filename.txt.

7
ответ дан Cees Timmerman 23 November 2019 в 02:19
поделиться

Это - решение, которое я в конечном счете использовал:

import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)

def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(c for c in cleanedFilename if c in validFilenameChars)

Вызов unicodedata.normalize заменяет символы с диакритикой безударным эквивалентом, который лучше, чем простое разделение их. После этого все запрещенные символы удалены.

Мое решение не предварительно ожидает известную строку для предотвращения возможных запрещенных имен файлов, потому что я знаю, что они не могут произойти, учитывая мой конкретный формат имени файла. Более общее решение должно было бы сделать так.

19
ответ дан Sophie Gage 23 November 2019 в 02:19
поделиться

ОБНОВЛЕНИЕ

Все ссылки в этом ответе 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 вы можете кодировать и декодировать, чтобы вы могли снова получить исходное имя файла.

Но в зависимости от варианта использования вам может быть лучше сгенерировать случайное имя файла и сохранить метаданные в отдельном файле или БД.

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'

ОРИГИНАЛЬНЫЙ ОТВЕТ LINKROTTEN :

Проект bobcat содержит модуль python, который делает именно это.

Он не совсем надежен, см. Этот пост и этот ответ .

Итак, как отмечалось: кодировка base64 , вероятно, лучше, если удобочитаемость не имеет значения.

0
ответ дан 23 November 2019 в 02:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: