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.
os.path
faktycznie ładuje inną bibliotekę w zależności od systemu operacyjnego (patrz druga uwaga w dokumentacji ). Więc jeśli funkcja cytowania została zaimplementowanaos.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.Odpowiedzi:
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.Jest więcej, ale pominąłem to, ponieważ nie odnosi się to do osłaszczenia, ale ucieczki.
źródło
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.slugify
Funkcja została przeniesiona do django / utils / text.py i że plik zawiera równieżget_valid_filename
funkcję.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.
źródło
valid_chars = frozenset(valid_chars)
nie zaszkodzi. Jest 1,5 razy szybszy, jeśli zostanie zastosowany do allcharów."CON"
w systemie Windows spowoduje kłopoty ...Możesz używać rozumienia listy razem z metodami łańcuchowymi.
źródło
filename = "".join(i for i in s if i not in "\/:*?<>|")
"".join( x for x in s if (x.isalnum() or x in "._- "))
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.
Aktualizacja : Zmieniono na podstawie komentarza Matthew.
źródło
your_string
musi być tablica bajtów lub wynikencode('ascii')
tego działania.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')
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.
źródło
Na Github jest ładny projekt o nazwie python-slugify :
Zainstalować:
Następnie użyj:
źródło
test.txt
dostaje,test-txt
co jest za dużo.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:
(Zobacz https://github.com/django/django/blob/master/django/utils/text.py )
źródło
django.utils.text import get_valid_filename
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 漢語.Oto rozwiązanie, z którego ostatecznie skorzystałem:
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.
źródło
Należy pamiętać, że tak naprawdę nie ma żadnych ograniczeń dotyczących nazw plików w systemach uniksowych innych niż
Cała reszta to uczciwa gra.
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;)
źródło
W jednej linii:
możesz także wstawić znak „_”, aby był bardziej czytelny (na przykład w przypadku zamiany ukośników)
źródło
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ć.
W wyniku tego dojdzie do uchwytu pliku do /tmp/filename.txt.
źródło
Nie obsługuje pustych ciągów, specjalnych nazw plików („nul”, „con” itp.).
źródło
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.
źródło
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.
źródło
osopen
działania na jednym komputerze.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:
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.
źródło
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:
Przykładowy kod:
Wynik:
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
none
tutaj).źródło
Odpowiedź zmodyfikowana dla Pythona 3.6
źródło
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.
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,
if
aby zawierała separator ścieżki os , jest banalna .źródło
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:
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ę.
źródło
Nie do końca to, o co prosił OP, ale tego właśnie używam, ponieważ potrzebuję unikalnych i odwracalnych konwersji:
Wynik jest „nieco” czytelny, przynajmniej z punktu widzenia sysadmin.
źródło
def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
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 :
źródło
Jestem pewien, że nie jest to świetna odpowiedź, ponieważ modyfikuje ciąg, który zapętla, ale wydaje się, że działa dobrze:
źródło
"".join( x for x in s if (x.isalnum() or x in "._- "))
w komentarzach do tego postuAKTUALIZACJA
Wszystkie linki zepsute nie do naprawienia w tej 6-letniej odpowiedzi.
Poza tym nie zrobiłbym tego w ten sposób, po prostu
base64
kodowałbym lub upuszczał niebezpieczne znaki. Przykład Python 3:Dzięki
base64
moż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.
ORYGINALNA ODPOWIEDŹ LINKROTTEN :
bobcat
Projekt zawiera moduł Pythona, który robi tylko to.Nie jest do końca solidny, zobacz ten post i tę odpowiedź .
Jak już wspomniano:
base64
kodowanie jest prawdopodobnie lepszym pomysłem, jeśli czytelność nie ma znaczenia.źródło