Zamienić ciąg w prawidłową nazwę pliku?


298

Mam ciąg, którego chcę użyć jako nazwę pliku, więc chcę usunąć wszystkie znaki, które nie byłyby dozwolone w nazwach plików, za pomocą Pythona.

Wolę być surowy niż inaczej, więc powiedzmy, że chcę zachować tylko litery, cyfry i mały zestaw innych znaków, takich jak "_-.() ". Jakie jest najbardziej eleganckie rozwiązanie?

Nazwa pliku musi być poprawna na wielu systemach operacyjnych (Windows, Linux i Mac OS) - jest to plik MP3 w mojej bibliotece z tytułem utworu jako nazwą pliku i jest współdzielony i tworzony z kopii zapasowej między 3 komputerami.


17
Czy nie należy tego wbudować w moduł os.path?
endolith

2
Być może, chociaż jej przypadek użycia wymagałby jednej bezpiecznej ścieżki na wszystkich platformach, nie tylko bieżącej, która jest czymś, czego os.path nie jest w stanie obsłużyć.
javawizard

2
Aby rozwinąć powyższy komentarz: obecny projekt os.pathfaktycznie ładuje inną bibliotekę w zależności od systemu operacyjnego (patrz druga uwaga w dokumentacji ). Więc jeśli funkcja cytowania została zaimplementowana os.path, może zacytować ciąg znaków dla POSIX-safety podczas uruchamiania w systemie POSIX lub dla bezpieczeństwa Windows podczas uruchamiania w systemie Windows. Wynikowa nazwa pliku niekoniecznie musi być poprawna zarówno w systemie Windows, jak i POSIX, o co pyta pytanie.
dshepherd

Odpowiedzi:


164

Możesz spojrzeć na środowisko Django i dowiedzieć się, jak tworzą „ślimak” z dowolnego tekstu. Ślimak jest przyjazny dla adresów URL i nazw plików.

Narzędzia tekstowe Django definiują funkcję, slugify() prawdopodobnie jest to złoty standard dla tego rodzaju rzeczy. Zasadniczo ich kod jest następujący.

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))
    # ...
    return value

Jest więcej, ale pominąłem to, ponieważ nie odnosi się to do osłaszczenia, ale ucieczki.


11
Ostatni wiersz powinien brzmieć: wartość = Unicode (re.sub ('[- \ s] +', '-', wartość))
Joseph Turian,

1
Dzięki - mogłem coś przeoczyć, ale dostaję: „normalizacja () argument 2 musi być Unicode, a nie str”
Alex Cook

„argument 2 normalizacji ()”. Oznacza value. Jeśli wartością musi być Unicode, musisz upewnić się, że jest to rzeczywiście Unicode. Lub. Możesz pominąć normalizację Unicode, jeśli twoja rzeczywista wartość jest w rzeczywistości łańcuchem ASCII.
S.Lott,

8
W przypadku, gdy ktoś nie zauważył pozytywnej strony tego podejścia jest to, że nie tylko usuwa on znaki inne niż alfa, ale próbuje najpierw znaleźć dobre zamienniki (poprzez normalizację NFKD), więc é staje się e, indeks górny 1 staje się normalna 1 itd. Dzięki
Michael Scott Cuthbert

48
slugifyFunkcja została przeniesiona do django / utils / text.py i że plik zawiera również get_valid_filenamefunkcję.
Denilson Sá Maia,

104

To podejście do białej listy (tzn. Zezwalanie tylko na znaki obecne w valid_chars) będzie działać, jeśli nie ma ograniczeń w formatowaniu plików lub kombinacji prawidłowych znaków, które są nielegalne (np. „..”), na przykład, co mówisz pozwoli na nazwę pliku o nazwie „. txt”, która moim zdaniem nie jest poprawna w systemie Windows. Ponieważ jest to najprostsze podejście, które spróbuję usunąć białe znaki z prawidłowych znaków i wstawić znany prawidłowy ciąg znaków w przypadku błędu, każde inne podejście będzie musiało wiedzieć o tym, co jest dozwolone, gdzie radzić sobie z ograniczeniami nazewnictwa plików systemu Windows, a tym samym być o wiele bardziej złożony.

>>> 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'

7
valid_chars = frozenset(valid_chars)nie zaszkodzi. Jest 1,5 razy szybszy, jeśli zostanie zastosowany do allcharów.
jfs,

2
Ostrzeżenie: mapuje dwa różne ciągi do tego samego ciągu >>> importuj ciąg >>> valid_chars = "- . ()% S% s"% (string.ascii_letters, string.digits) >>> valid_chars '- . () abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '>>> nazwa_pliku = "a.com/hello/world" >>>' '.join (c dla c w nazwie pliku, jeśli cw poprawnych_znakach)' a.comhelloworld '>>> nazwa_pliku = a.com ">>> '' .join (c dla c w nazwie pliku, jeśli c w prawidłowych_znakach) 'a.comhelloworld' >>>
Robert King

3
Nie wspominając o tym, że nazwanie pliku "CON"w systemie Windows spowoduje kłopoty ...
Nathan Osman,

2
Nieznaczne przegrupowanie sprawia, że ​​określenie postaci zastępczej jest proste. Najpierw oryginalna funkcjonalność: „.join (c, jeśli c w poprawnych_znakach” ”dla cw nazwie pliku) lub z podstawionym znakiem lub łańcuchem dla każdego niepoprawnego znaku:„ ”.join (c, jeśli cw poprawnym_znakach„. ”Dla cw nazwie pliku)
PeterVermont

101

Możesz używać rozumienia listy razem z metodami łańcuchowymi.

>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'

3
Pamiętaj, że możesz pominąć nawiasy kwadratowe. W takim przypadku wyrażenie łączące jest przekazywane do przyłączenia, co oszczędza kroku tworzenia listy nieużywanej w inny sposób.
Oben Sonne

31
+1 Podobało mi się to. Nieznaczna modyfikacja, którą zrobiłem: „” .join ([x jeśli x.isalnum () else „_” dla xws]) - dałoby wynik, w którym nieprawidłowe elementy są _, jakby były puste. Może to ktoś inny.
Eddie Parker,

12
To rozwiązanie jest świetne! Dokonałem jednak drobnej modyfikacji:filename = "".join(i for i in s if i not in "\/:*?<>|")
Alex Krycek,

1
Niestety nie pozwala nawet na spacje i kropki, ale podoba mi się ten pomysł.
tiktak,

9
@tiktak: aby (także) dopuszczać spacje, kropki i podkreślenia, na które możesz pójść"".join( x for x in s if (x.isalnum() or x in "._- "))
hardmooth

95

Jaki jest powód używania ciągów jako nazw plików? Jeśli czytelność dla ludzi nie jest czynnikiem, wybrałbym moduł base64, który może generować bezpieczne łańcuchy systemu plików. Nie będzie czytelny, ale nie będziesz musiał radzić sobie z kolizjami i jest odwracalny.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

Aktualizacja : Zmieniono na podstawie komentarza Matthew.


1
Oczywiście jest to najlepsza odpowiedź, jeśli tak jest.
user32141,

60
Ostrzeżenie! Kodowanie base64 domyślnie zawiera znak „/” jako prawidłowe dane wyjściowe, które nie są poprawne w nazwach plików w wielu systemach. Zamiast tego użyj base64.urlsafe_b64encode (twój_ciąg)
Matthew

15
W rzeczywistości czytelność dla ludzi jest prawie zawsze czynnikiem, nawet jeśli tylko do celów debugowania.
static_rtti

5
W Pythonie 3 your_stringmusi być tablica bajtów lub wynik encode('ascii')tego działania.
Noumenon,

4
def url2filename(url): url = url.encode('UTF-8') return base64.urlsafe_b64encode(url).decode('UTF-8') def filename2url(f): return base64.urlsafe_b64decode(f).decode('UTF-8')
JeffProd,

40

Aby jeszcze bardziej skomplikować sprawę, nie ma gwarancji, że otrzymasz prawidłową nazwę pliku po usunięciu nieprawidłowych znaków. Ponieważ dozwolone znaki różnią się w różnych nazwach plików, konserwatywne podejście może w efekcie zmienić prawidłową nazwę w niepoprawną. Możesz dodać specjalną obsługę w przypadkach, gdy:

  • Ciąg zawiera wszystkie nieprawidłowe znaki (pozostawiając ci pusty ciąg)

  • Otrzymasz ciąg o specjalnym znaczeniu, np. „.” lub „..”

  • W systemie Windows niektóre nazwy urządzeń są zastrzeżone. Na przykład nie można utworzyć pliku o nazwie „nul”, „nul.txt” (lub nul.wszystko w rzeczywistości). Nazwy zastrzeżone:

    CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 i LPT9

Prawdopodobnie możesz obejść te problemy, przygotowując ciąg znaków do nazw plików, które nigdy nie mogą spowodować jednego z tych przypadków, i usuwając nieprawidłowe znaki.


24

Na Github jest ładny projekt o nazwie python-slugify :

Zainstalować:

pip install python-slugify

Następnie użyj:

>>> from slugify import slugify
>>> txt = "This\ is/ a%#$ test ---"
>>> slugify(txt)
'this-is-a-test'

2
Podoba mi się ta biblioteka, ale nie jest tak dobra, jak myślałem. Wstępne testowanie jest w porządku, ale konwertuje również kropki. Więc test.txtdostaje, test-txtco jest za dużo.
therealmarv,

23

Tak jak odpowiedział S.Lott , możesz sprawdzić w Django Framework, w jaki sposób konwertują ciąg na prawidłową nazwę pliku.

Najnowsza i zaktualizowana wersja znajduje się w utils / text.py i definiuje „get_valid_filename”, który wygląda następująco:

def get_valid_filename(s):
    s = str(s).strip().replace(' ', '_')
    return re.sub(r'(?u)[^-\w.]', '', s)

(Zobacz https://github.com/django/django/blob/master/django/utils/text.py )


4
dla leniwych już na django:django.utils.text import get_valid_filename
theannouncer

2
Jeśli nie znasz wyrażenia regularnego, re.sub(r'(?u)[^-\w.]', '', s)usuwa wszystkie znaki, które nie są literami, nie cyframi (0–9), nie podkreśleniem („_”), ani myślnikiem („-”), ani kropką („.” ). „Litery” obejmują tutaj wszystkie litery Unicode, takie jak 漢語.
cowlinator

3
Możesz także sprawdzić długość: nazwy plików są ograniczone do 255 znaków (lub, no wiesz, 32; w zależności od FS)
Matthias Winkelmann

19

Oto rozwiązanie, z którego ostatecznie skorzystałem:

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)

Wywołanie unicodedata.normalize zastępuje znaki akcentowane nieakcentowanym odpowiednikiem, co jest lepsze niż zwykłe ich usuwanie. Następnie wszystkie niedozwolone postacie są usuwane.

Moje rozwiązanie nie poprzedza znanego łańcucha, aby uniknąć możliwych niedozwolonych nazw plików, ponieważ wiem, że nie mogą one wystąpić, biorąc pod uwagę mój szczególny format nazwy pliku. Potrzebne byłoby bardziej ogólne rozwiązanie.


powinieneś być w stanie użyć uuid.uuid4 () dla unikalnego prefiksu
slf

6
futerał na wielbłąda .. ahh
szalony jeż

Czy można to edytować / aktualizować do pracy z Pythonem 3.6?
Wavesailor

13

Należy pamiętać, że tak naprawdę nie ma żadnych ograniczeń dotyczących nazw plików w systemach uniksowych innych niż

  • Nie może zawierać \ 0
  • Nie może zawierać /

Cała reszta to uczciwa gra.

$ touch ”
> nawet multilinię
> haha
> ^ [[31m czerwony ^ [[0m
> zło ”
$ ls -la 
-rw-r - r-- 0 listopada 17 23:39? nawet multiline? haha? [31m czerwony? [0m? zły
$ ls -lab
-rw-r - r-- 0 listopada 17 23:39 \ neven \ multiline \ nhaha \ n \ 033 [31m \ red \ \ 033 [0m \ nevil
$ perl -e 'dla moich $ i (glob (q {./* nawet *})) {print $ i; } ”
./
nawet multilinię
ha ha
 czerwony 
zło

Tak, właśnie zapisałem Kody Kolorów ANSI w nazwie pliku i sprawiłem, że zadziałały.

Dla rozrywki wstaw znak BEL w nazwie katalogu i oglądaj zabawę, która pojawia się po włożeniu do niego płyty CD;)


OP stwierdza, że ​​„nazwa pliku musi być poprawna na wielu systemach operacyjnych”
cowlinator,

1
@cowlinator, że wyjaśnienie zostało dodane 10 godzin po opublikowaniu mojej odpowiedzi :) Sprawdź dziennik edycji OP.
Kent Fredric

12

W jednej linii:

valid_file_name = re.sub('[^\w_.)( -]', '', any_string)

możesz także wstawić znak „_”, aby był bardziej czytelny (na przykład w przypadku zamiany ukośników)


7

Możesz użyć metody re.sub (), aby zastąpić cokolwiek innego niż „filelike”. Ale w efekcie każda postać może być ważna; więc nie ma żadnych gotowych funkcji (wierzę), aby to zrobić.

import re

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

W wyniku tego dojdzie do uchwytu pliku do /tmp/filename.txt.


5
Potrzebujesz myślnika, aby przejść pierwszy w dopasowaniu grupowym, aby nie pojawiał się jako zakres. re.sub ('[^ - a-zA-Z0-9 _. ()] +', '', str)
phord

7
>>> 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'

Nie obsługuje pustych ciągów, specjalnych nazw plików („nul”, „con” itp.).


+1 dla tabel translacji jest zdecydowanie najbardziej wydajną metodą. W przypadku specjalnych nazw / opróżnień plików wystarczy prosta kontrola warunków wstępnych, a w przypadku okresów obcych również prosta korekta.
Christian Witts,

1
Podczas gdy tłumaczenie jest nieco bardziej wydajne niż wyrażenie regularne, ten czas najprawdopodobniej zmarnuje się, jeśli faktycznie spróbujesz otworzyć plik, co bez wątpienia zamierzasz zrobić. Dlatego wolę bardziej czytelne rozwiązanie
wyrażenia regularnego

Martwię się również o czarną listę. To prawda, że ​​jest to czarna lista oparta na białej liście, ale nadal. Wydaje się mniej ... bezpieczny. Skąd wiesz, że „Allchary” są faktycznie ukończone?
isaaclw

@isaaclw: '.translate ()' przyjmuje łańcuch 256-znakowy jako tabelę translacji (tłumaczenie bajt-na-bajt). „.maketrans ()” tworzy taki ciąg. Wszystkie wartości są objęte; jest to podejście oparte wyłącznie na białej liście
jfs

Co z nazwą pliku „.” (pojedyncza kropka). Nie działałoby to na Uniksach, ponieważ obecny katalog używa tej nazwy.
Finn Årup Nielsen

6

Chociaż musisz być ostrożny. W swoim wstępie nie jest wyraźnie powiedziane, jeśli patrzysz tylko na język łaciński. Niektóre słowa mogą stać się bez znaczenia lub inne znaczenie, jeśli odkażą je tylko przy użyciu znaków ascii.

wyobraź sobie, że masz „forêt poésie” (poezja leśna), a Twoja dezynfekcja może dać „fort-posie” (silny + coś bez znaczenia)

Gorzej, jeśli masz do czynienia z chińskimi znakami.

„下 北 沢” twój system może skończyć „---”, co po pewnym czasie kończy się niepowodzeniem i nie jest zbyt pomocne. Jeśli więc zajmujesz się tylko plikami, zachęcam do nazwania ich ogólnym łańcuchem, który kontrolujesz, lub do zachowania znaków bez zmian. W przypadku identyfikatorów URI mniej więcej tak samo.


6

Dlaczego nie po prostu zawinąć „osopen” za pomocą try / wyjątkiem i pozwolić systemowi operacyjnemu ustalić, czy plik jest prawidłowy?

Wydaje się, że to znacznie mniej pracy i jest ważne bez względu na używany system operacyjny.


5
Czy to prawda, że ​​nazwa? Mam na myśli, że jeśli system operacyjny nie jest zadowolony, to nadal musisz coś zrobić, prawda?
jeromej

1
W niektórych przypadkach system operacyjny / język może po cichu przekształcić nazwę pliku w alternatywną formę, ale kiedy zrobisz listę katalogów, uzyskasz inną nazwę. A to może prowadzić do problemu „kiedy piszę plik, jest tam, ale kiedy szukam pliku, nazywa się to czymś innym”. (Mówię o zachowaniu, o którym słyszałem na VAX ...)
Kent Fredric,

Ponadto „Nazwa pliku musi być poprawna w wielu systemach operacyjnych”, czego nie można wykryć w przypadku osopendziałania na jednym komputerze.
LarsH

5

Innym problemem, który nie został jeszcze rozwiązany w innych komentarzach, jest pusty ciąg znaków, który oczywiście nie jest prawidłową nazwą pliku. Możesz także skończyć z pustym łańcuchem po usunięciu zbyt wielu znaków.

Co z zastrzeżonymi nazwami plików Windows i problemami z kropkami, najbezpieczniejsza odpowiedź na pytanie „jak znormalizować prawidłową nazwę pliku na podstawie danych wprowadzonych przez dowolnego użytkownika?” to „nawet nie zawracaj sobie głowy próbą”: jeśli możesz znaleźć inny sposób, aby tego uniknąć (np. używając całkowitych kluczy podstawowych z bazy danych jako nazw plików), zrób to.

Jeśli musisz i naprawdę musisz dopuścić spacje i „.” w przypadku rozszerzeń plików jako części nazwy spróbuj czegoś takiego:

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

Nawet tego nie można zagwarantować, zwłaszcza w przypadku nieoczekiwanych systemów operacyjnych - na przykład system RISC nie znosi spacji i używa „”. jako separator katalogów.


4

Podobało mi się tutaj podejście python-slugify, ale usuwało ono również kropki, co nie było pożądane. Zoptymalizowałem go więc do przesłania czystej nazwy pliku do s3 w ten sposób:

pip install python-slugify

Przykładowy kod:

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

Wynik:

>>> clean_filename
'very-unsafe-file-name-haha.txt'

Jest to tak bezpieczne, działa z nazwami plików bez rozszerzenia, a nawet działa tylko z nazwami plików niebezpiecznych znaków (wynik jest nonetutaj).


1
Podoba mi się to, nie wymyślaj na nowo koła, nie importuj całego frameworka Django, jeśli go nie potrzebujesz, nie wklejaj bezpośrednio kodu, jeśli nie zamierzasz go utrzymywać w przyszłości, a wygenerowane ciągi próbują dopasowuje podobne litery do bezpiecznych, dzięki czemu nowy ciąg znaków jest łatwiejszy do odczytania.
vicenteherrera

1
Aby użyć podkreślenia zamiast myślnika: name = slugify (s, separator = '_')
vicenteherrera

3

Odpowiedź zmodyfikowana dla Pythona 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)

Czy mógłbyś szczegółowo wyjaśnić swoją odpowiedź?
Serenity,

To ta sama odpowiedź, którą zaakceptowała Sophie Gage. Ale został zmodyfikowany do pracy w Pythonie 3.6
Jean-Robin Tremblay

2

Zdaję sobie sprawę, że jest wiele odpowiedzi, ale najczęściej polegają one na wyrażeniach regularnych lub modułach zewnętrznych, więc chciałbym wrzucić własną odpowiedź. Funkcja czysto pythonowa, nie wymaga zewnętrznego modułu, nie jest używane wyrażenie regularne. Moje podejście nie polega na czyszczeniu nieprawidłowych znaków, ale zezwalaniu tylko na prawidłowe.

def normalizefilename(fn):
    validchars = "-_.() "
    out = ""
    for c in fn:
      if str.isalpha(c) or str.isdigit(c) or (c in validchars):
        out += c
      else:
        out += "_"
    return out    

jeśli chcesz, możesz dodać własne prawidłowe znaki do validchars zmienne , takie jak litery narodowe, które nie występują w alfabecie angielskim. Jest to coś, czego możesz chcieć lub nie: niektóre systemy plików, które nie działają na UTF-8, mogą nadal mieć problemy z znakami spoza ASCII.

Ta funkcja służy do sprawdzania poprawności pojedynczej nazwy pliku, więc zastąpi separatory ścieżek wartością _, uznając je za nieprawidłowe znaki. Jeśli chcesz to dodać, modyfikacja tak, ifaby zawierała separator ścieżki os , jest banalna .


1

Większość tych rozwiązań nie działa.

„/ hello / world” -> „helloworld”

„/ helloworld” / -> „helloworld”

Na ogół nie jest to, co chcesz, powiedz, że zapisujesz html dla każdego linku, zastąpisz html dla innej strony.

Ogłaszam taki dyktand jak:

{'helloworld': 
    (
    {'/hello/world': 'helloworld', '/helloworld/': 'helloworld1'},
    2)
    }

2 oznacza liczbę, która powinna być dołączona do następnej nazwy pliku.

Za każdym razem szukam nazwy pliku ze słownika. Jeśli go nie ma, tworzę nowy, dołączając w razie potrzeby maksymalną liczbę.


Uwaga: jeśli używasz helloworld1, musisz również sprawdzić, czy helloworld1 nie jest używany i tak dalej ...
Robert King

1

Nie do końca to, o co prosił OP, ale tego właśnie używam, ponieważ potrzebuję unikalnych i odwracalnych konwersji:

# 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+-_ .'))

Wynik jest „nieco” czytelny, przynajmniej z punktu widzenia sysadmin.


Opakowanie do tego bez spacji w nazwach plików:def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
SpeedCoder5

1

Jeśli nie masz nic przeciwko instalowaniu pakietu, powinno to być przydatne: https://pypi.org/project/pathvalidate/

Od https://pypi.org/project/pathvalidate/#sanitize-a-filename :

from pathvalidate import sanitize_filename

fname = "fi:l*e/p\"a?t>h|.t<xt"
print(f"{fname} -> {sanitize_filename(fname)}\n")
fname = "\0_a*b:c<d>e%f/(g)h+i_0.txt"
print(f"{fname} -> {sanitize_filename(fname)}\n")

Wynik

fi:l*e/p"a?t>h|.t<xt -> filepath.txt
_a*b:c<d>e%f/(g)h+i_0.txt -> _abcde%f(g)h+i_0.txt

0

Jestem pewien, że nie jest to świetna odpowiedź, ponieważ modyfikuje ciąg, który zapętla, ale wydaje się, że działa dobrze:

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, '')

Znalazłem to "".join( x for x in s if (x.isalnum() or x in "._- "))w komentarzach do tego postu
SergioAraujo

0

AKTUALIZACJA

Wszystkie linki zepsute nie do naprawienia w tej 6-letniej odpowiedzi.

Poza tym nie zrobiłbym tego w ten sposób, po prostu base64kodowałbym lub upuszczał niebezpieczne znaki. Przykład Python 3:

import re
t = re.compile("[a-zA-Z0-9.,_-]")
unsafe = "abc∂éåß®∆˚˙©¬ñ√ƒµ©∆∫ø"
safe = [ch for ch in unsafe if t.match(ch)]
# => 'abc'

Dzięki base64możesz kodować i dekodować, dzięki czemu możesz ponownie pobrać oryginalną nazwę pliku.

Ale w zależności od przypadku użycia lepiej byłoby wygenerować losową nazwę pliku i przechowywać metadane w osobnym pliku lub 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'

ORYGINALNA ODPOWIEDŹ LINKROTTEN :

bobcatProjekt zawiera moduł Pythona, który robi tylko to.

Nie jest do końca solidny, zobacz ten post i tę odpowiedź .

Jak już wspomniano: base64kodowanie jest prawdopodobnie lepszym pomysłem, jeśli czytelność nie ma znaczenia.


Wszystkie linki nie żyją. Człowieku, zrób coś.
The Peaceful Coder,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.