एक वैध फ़ाइल नाम में एक स्ट्रिंग मुड़ें?


298

मेरे पास एक स्ट्रिंग है जिसे मैं एक फ़ाइल नाम के रूप में उपयोग करना चाहता हूं, इसलिए मैं उन सभी पात्रों को हटाना चाहता हूं जिन्हें पायथन का उपयोग करके फ़ाइल नाम में अनुमति नहीं दी जाएगी।

मैं अन्यथा के बजाय सख्त होना चाहूँगा, इसलिए मान लें कि मैं केवल अक्षरों, अंकों और अन्य वर्णों के एक छोटे समूह को बनाए रखना चाहता हूं "_-.() "। सबसे सुरुचिपूर्ण समाधान क्या है?

फ़ाइलनाम को कई ऑपरेटिंग सिस्टम (विंडोज, लिनक्स और मैक ओएस) पर मान्य होने की आवश्यकता है - यह मेरी लाइब्रेरी में एमपी 3 फ़ाइल है जिसे फ़ाइल नाम के रूप में फ़ाइल नाम के साथ रखा गया है, और इसे 3 मशीनों के बीच साझा और समर्थित किया गया है।


17
यह os.path मॉड्यूल में नहीं बनाया जाना चाहिए?
एंडोलिथ

2
शायद, हालांकि उसके उपयोग के मामले में सभी प्लेटफार्मों पर सुरक्षित एक एकल मार्ग की आवश्यकता होती है , न कि केवल वर्तमान एक, जो कुछ है। ओएस को संभालने के लिए डिज़ाइन नहीं किया गया है।
ज्वालामुखी जू

2
उपरोक्त टिप्पणी पर विस्तार करने के लिए: os.pathवास्तव में ओएस के आधार पर एक अलग पुस्तकालय लोड करता है ( प्रलेखन में दूसरा नोट देखें )। इसलिए यदि इसमें एक कोटिंग फंक्शन लागू किया गया था, os.pathतो केवल POSIX सिस्टम पर चलने पर या विंडोज़ पर चलने के दौरान विंडोज़-सेफ्टी के लिए स्ट्रिंग को POSIX-safety के लिए उद्धृत किया जा सकता था । परिणामस्वरूप फ़ाइल नाम आवश्यक रूप से दोनों विंडोज़ और POSIX पर मान्य नहीं होगा, जो कि प्रश्न पूछता है।
dshepherd

जवाबों:


164

आप Django ढांचे को देख सकते हैं कि वे कैसे मनमाने ढंग से पाठ से "स्लग" बनाते हैं। एक स्लग URL- और फ़ाइल नाम के अनुकूल है।

Django पाठ बर्तन एक फ़ंक्शन को परिभाषित करते हैं, 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))
    # ...
    return value

वहाँ और भी है, लेकिन मैंने इसे छोड़ दिया, क्योंकि यह दासता को संबोधित नहीं करता है, लेकिन बच रहा है।


11
अंतिम पंक्ति इस प्रकार होनी चाहिए: मान = यूनिकोड (re.sub ('[- \ s] +', '-', मान))
यूसुफ टुरियन

1
धन्यवाद - मुझे कुछ याद आ रहा है, लेकिन मुझे मिल रहा है: "सामान्यीकृत करें () तर्क 2 को यूनिकोड होना चाहिए, स्ट्रगल नहीं"
एलेक्स कुक

"सामान्यीकृत करें () तर्क 2"। का मतलब है value। यदि मान यूनिकोड होना चाहिए, तो, आपको यह सुनिश्चित करना होगा कि यह वास्तव में यूनिकोड है। या। यदि आपका वास्तविक मूल्य वास्तव में ASCII स्ट्रिंग है तो आप यूनिकोड सामान्यीकरण को छोड़ना चाह सकते हैं।
एस.लॉट

8
यदि किसी ने इस दृष्टिकोण के सकारात्मक पक्ष पर ध्यान नहीं दिया है, तो यह केवल गैर-अल्फा वर्णों को नहीं हटाता है, लेकिन पहले (NFKD सामान्यीकरण के माध्यम से) अच्छे विकल्प खोजने का प्रयास करता है, इसलिए é ई बन जाता है, एक सुपरस्क्रिप्ट 1 बन जाता है सामान्य 1, आदि धन्यवाद
माइकल स्कॉट कटहबर्ट

48
slugifyसमारोह के लिए ले जाया गया है Django / utils / text.py , और उस फ़ाइल को भी एक होता है get_valid_filenameकार्य करते हैं।
डेनिलसन सा मैया

104

यह श्वेतसूची दृष्टिकोण (अर्थात, मान्य_चरों में मौजूद केवल वर्णों को अनुमति देता है) काम करेगा यदि फाइलों के प्रारूपण या वैध वर्णों के संयोजन की सीमाएँ नहीं हैं जो अवैध हैं (जैसे ".."), उदाहरण के लिए, आप क्या कहते हैं एक फ़ाइल नाम "txt" की अनुमति देगा जो मुझे लगता है कि विंडोज पर मान्य नहीं है। जैसा कि यह सबसे सरल तरीका है मैं मान्य_चर्स से व्हाट्सएप को हटाने की कोशिश करूंगा और त्रुटि के मामले में एक ज्ञात वैध स्ट्रिंग को प्रस्तुत करूंगा, किसी भी अन्य दृष्टिकोण को जानना होगा कि विंडोज फाइल के नामकरण की सीमाओं का सामना करने की अनुमति कहां है और इस प्रकार क्या है बहुत अधिक जटिल।

>>> 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)दुख नहीं होगा। अगर यह अल्चर्स पर लागू होता है तो यह 1.5 गुना तेज है।
11:17

2
चेतावनी: यह एक ही स्ट्रिंग के दो अलग-अलग तारों को मैप करता है >>> आयात स्ट्रिंग >>> मान्य_चर्स = "- । ()% s% s"% (string.ascii_letters, string.digits) >>> valid_chars '- ()। abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012323676789 '>>> फ़ाइल नाम = "a.com/hello/world ">>' .join (c फ़ाइल में c के लिए यदि c। के लिए कोई खाता नहीं है hell ahelloworld.nic.in)। ">>> ''। जोंइन (सी के लिए सी में फाइलनाम में अगर c मान्य_चारों में है) '' a.comhelloworld '>>>
राजा

3
"CON"विंडोज पर एक फ़ाइल का नामकरण करने से आपको परेशानी नहीं होगी ...
नाथन उस्मान

2
एक मामूली पुनर्व्यवस्था एक स्थानापन्न चरित्र को स्पष्ट करती है। पहली मूल कार्यक्षमता: '' .join (c अगर मान्य_चर्स में c है तो '' ग के लिए फ़ाइलनाम में) या हर अमान्य चरित्र के लिए एक प्रतिस्थापित चरित्र या स्ट्रिंग के साथ: '' .join (c अगर c में मान्य_चर्चा किसी और में है '')। फ़ाइल नाम में c)
पीटरविमोंट

101

आप स्ट्रिंग विधियों के साथ सूची समझ का उपयोग कर सकते हैं।

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

3
ध्यान दें कि आप वर्ग कोष्ठक को छोड़ सकते हैं। इस मामले में एक जनरेटर अभिव्यक्ति को शामिल होने के लिए पारित किया जाता है, जो अन्यथा अप्रयुक्त सूची बनाने के चरण को बचाता है।
ओबेन सोनाने

31
+1 इसे पसंद किया। थोड़ा सा संशोधन मैंने किया है: "" .join ([x अगर x.isalnum () और "_" के लिए x s में)] - परिणाम देगा जहाँ अमान्य आइटम _ हैं, जैसे वे खाली हैं। शायद किसी और को थाल्स थापना।
एडी पार्कर

12
यह समाधान महान है! मैंने हालांकि थोड़ा संशोधन किया:filename = "".join(i for i in s if i not in "\/:*?<>|")
एलेक्स क्राइसेक

1
दुर्भाग्य से यह भी रिक्त स्थान और डॉट्स की अनुमति नहीं देता है, लेकिन मुझे यह विचार पसंद है।
तैक्तक

9
@tiktak: (भी) रिक्त स्थान, डॉट्स और अंडरस्कोर की अनुमति दें जिसके लिए आप जा सकते हैं"".join( x for x in s if (x.isalnum() or x in "._- "))
हार्डमैट

95

फ़ाइल नाम के रूप में स्ट्रिंग्स का उपयोग करने का कारण क्या है? यदि मानव पठनीयता एक कारक नहीं है, तो मैं बेस 64 मॉड्यूल के साथ जाऊंगा जो फाइल सिस्टम सुरक्षित तारों का उत्पादन कर सकता है। यह पठनीय नहीं होगा, लेकिन आपको टकराव से निपटना नहीं होगा और यह प्रतिवर्ती है।

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

अपडेट : मैथ्यू टिप्पणी के आधार पर परिवर्तित।


1
जाहिर है अगर यह मामला है तो यह सबसे अच्छा जवाब है।
user32141

60
चेतावनी! मूलभूत रूप से बेस 64 एनकोडिंग में वैध आउटपुट के रूप में "/" वर्ण शामिल है जो बहुत सारे सिस्टम पर फ़ाइल नाम में मान्य नहीं है। इसके बजाय base64.urlsafe_b64encode (your_string) का उपयोग करें
मैथ्यू

15
वास्तव में मानव पठनीयता लगभग हमेशा एक कारक है, भले ही केवल डिबगिंग उद्देश्यों के लिए।
static_rtti

5
पायथन 3 में your_stringएक बाइट सरणी या encode('ascii')काम करने के लिए इसके परिणाम की आवश्यकता है ।
नौमेनन

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')
जेफप्रोड

40

बस चीजों को और जटिल करने के लिए, आपको अमान्य वर्ण हटाकर केवल एक वैध फ़ाइल नाम प्राप्त करने की गारंटी नहीं है। चूंकि अलग-अलग फ़ाइलनाम पर वर्ण अलग-अलग हैं, इसलिए एक रूढ़िवादी दृष्टिकोण एक वैध नाम को अमान्य में बदल सकता है। आप उन मामलों के लिए विशेष हैंडलिंग जोड़ना चाहते हैं जहां:

  • स्ट्रिंग सभी अमान्य वर्ण हैं (खाली स्ट्रिंग के साथ आपको छोड़कर)

  • आप एक विशेष अर्थ के साथ एक स्ट्रिंग के साथ समाप्त होते हैं, जैसे "।" या ".."

  • खिड़कियों पर, कुछ डिवाइस के नाम आरक्षित हैं। उदाहरण के लिए, आप "nul", "nul.txt" (या वास्तव में nul.anything) नाम की कोई फ़ाइल नहीं बना सकते। आरक्षित नाम हैं:

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

आप शायद इन मुद्दों के इर्द-गिर्द कुछ ऐसे स्ट्रिंग को फिल्माते हैं, जो कभी भी इन मामलों में से एक में परिणाम नहीं कर सकते हैं, और अमान्य वर्णों को हटा सकते हैं।


24

गीथ पर एक अच्छा प्रोजेक्ट है जिसे अजगर-स्लगिफ़ कहा जाता है :

इंस्टॉल:

pip install python-slugify

फिर उपयोग करें:

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

2
मुझे यह लाइब्रेरी पसंद है लेकिन यह उतना अच्छा नहीं है जितना मैंने सोचा था। प्रारंभिक परीक्षण ठीक है लेकिन यह भी डॉट्स को रूपांतरित करता है। तो जो बहुत अधिक test.txtहो जाता test-txtहै।
उपचार

23

जैसे S.Lott ने उत्तर दिया, आप Django फ्रेमवर्क को देख सकते हैं कि कैसे वे एक स्ट्रिंग को एक वैध फ़ाइल नाम में परिवर्तित करते हैं।

सबसे हाल ही में और अपडेट किया गया संस्करण utils / text.py में पाया गया है, और "get_valid_filename" को परिभाषित करता है, जो इस प्रकार है:

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

( Https://github.com/django/django/blob/master/django/utils/text.py देखें )


4
पहले से ही django पर आलसी के लिए:django.utils.text import get_valid_filename
theannouncer 19

2
यदि आप regex से अपरिचित हैं, तो re.sub(r'(?u)[^-\w.]', '', s)सभी वर्णों को हटा देता है जो अक्षर नहीं हैं, अंक (0-9) नहीं, अंडरस्कोर ('_') नहीं, डैश नहीं ('-'), और अवधि नहीं (')। )। यहाँ "पत्र" में सभी यूनिकोड अक्षर शामिल हैं, जैसे कि "語।
काउलिनेटर

3
आप लंबाई के लिए भी जांच कर सकते हैं: फ़ाइल नाम 255 वर्णों तक सीमित हैं (या, आप जानते हैं, 32, FS के आधार पर)
मैथियास विंकेलमैन

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)

यूनिकोडेटा.नॉर्मलाइज कॉल अनौपचारिक समकक्ष के साथ उच्चारण वर्णों को बदल देता है, जो कि बस उन्हें बाहर निकालने से बेहतर है। उसके बाद सभी अस्वीकृत वर्ण हटा दिए जाते हैं।

मेरा समाधान संभव नापसंद फ़ाइल नाम से बचने के लिए एक ज्ञात स्ट्रिंग को प्रस्तुत नहीं करता है, क्योंकि मुझे पता है कि वे मेरे विशेष फ़ाइल नाम प्रारूप को नहीं दे सकते हैं। एक अधिक सामान्य समाधान के लिए ऐसा करने की आवश्यकता होगी।


आप अपने अद्वितीय उपसर्ग के लिए uuid.uuid4 () का उपयोग करने में सक्षम होना चाहिए
sl

6
ऊँट का मामला .. आह
नियोजित हेजहोग १ '

क्या इसे पायथन 3.6 के साथ काम करने के लिए संपादित / अपडेट किया जा सकता है?
वेवसैलर

13

ध्यान रखें, वास्तव में यूनिक्स सिस्टम पर फ़ाइल नाम पर कोई प्रतिबंध नहीं हैं

  • इसमें \ 0 नहीं हो सकता है
  • इसमें शामिल नहीं हो सकता है /

बाकी सब निष्पक्ष खेल है।

$ स्पर्श "
> यहां तक ​​कि बहुस्तरीय
> हा
> ^ [[31 मीटर रेड ^ [[0 मी
> बुराई "
$ एलएस -ला 
-rw-r - r-- 0 Nov 17 23:39? यहां तक ​​कि बहु? haha ​​?? [31 मीटर लाल? [0m) बुराई
$ ls -लैब
-rw-r - r-- 0 Nov 17 23:39 \ neven \ multiline \ nhaha \ n \ 033 [31m \ red \ \ 033 [0m \ nevil
$ perl -e 'मेरे $ i के लिए (glob (q {./* even *})) {प्रिंट $ i; } '
./
यहां तक ​​कि बहुस्तरीय
haha
 लाल 
बुराई

हां, मैंने अभी एएनएसआई कलर कोड एक फ़ाइल नाम में संग्रहीत किया है और उन्हें प्रभावी किया है।

मनोरंजन के लिए, बीईएल कैरेक्टर को डायरेक्टरी के नाम पर रखें और जब आप उसमें सीडी डालते हैं, तो उस फन को देखें;)


ओपी कहता है कि "फ़ाइलनाम को कई ऑपरेटिंग सिस्टम पर मान्य होने की आवश्यकता है"
काउललाइनर

1
@ जवाब देने के 10 घंटे बाद मेरे जवाब पोस्ट किए जाने पर स्पष्टीकरण को जोड़ा गया :) ओपी के एडिट लॉग की जाँच करें।
केंट फ्रेड्रिक

12

एक पंक्ति में:

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

आप इसे अधिक पठनीय बनाने के लिए '_' वर्ण भी डाल सकते हैं (उदाहरण के लिए स्लैश बदलने के मामले में)


7

आप कुछ भी "फाइललाइक" नहीं बदलने के लिए re.sub () विधि का उपयोग कर सकते हैं। लेकिन वास्तव में, हर चरित्र वैध हो सकता है; इसलिए कोई पूर्वनिर्मित कार्य नहीं हैं (मेरा मानना ​​है), इसे पूरा करने के लिए।

import re

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

/Tmp/filename.txt में एक फ़ाइलहैंडल होगा।


5
आपको समूह मिलान में पहले जाने के लिए डैश की आवश्यकता होती है ताकि यह एक सीमा के रूप में प्रकट न हो। re.sub ('[^ - a-zA-Z0-9 _। ()] +', '', स्ट्र)
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'

यह खाली तार, विशेष फ़ाइलनाम ('nul', 'con', आदि) को संभालता नहीं है।


अनुवाद तालिका के लिए +1, यह अब तक की सबसे कारगर विधि है। विशेष फ़ाइल नाम / शून्यता के लिए, एक साधारण पूर्व-स्थिति जांच पर्याप्त होगी और बाहरी समय के लिए यह एक साधारण सुधार भी होगा।
क्रिश्चियन विट

1
जबकि अनुवाद regexp की तुलना में थोड़ा अधिक कुशल है, उस समय सबसे अधिक संभावना होगी कि अगर आप वास्तव में फ़ाइल को खोलने का प्रयास करते हैं, जो कि कोई संदेह नहीं है कि आप करने का इरादा कर रहे हैं। इस प्रकार मैं ऊपर गंदगी की तुलना में अधिक पठनीय regexp समाधान पसंद करता हूं
nosatalian

मैं ब्लैक लिस्ट को लेकर भी चिंतित हूं। दी, यह एक ब्लैकलिस्ट है जो एक श्वेतसूची पर आधारित है, लेकिन फिर भी। यह कम लगता है ... सुरक्षित। आप कैसे जानते हैं कि "ऑलचर" वास्तव में पूर्ण है?
isaaclw

@isaaclw: '.translate ()' 256-स्ट्रिंग स्ट्रिंग को अनुवाद तालिका (बाइट-टू-बाइट अनुवाद) के रूप में स्वीकार करता है। '.maketrans ()' ऐसी स्ट्रिंग बनाता है। सभी मूल्य शामिल हैं; यह एक शुद्ध श्वेत सूची दृष्टिकोण है
JFS

फिल्म के नाम के बारे में क्या। ' (सिंगल डॉट)। यह यूनिक्स पर काम नहीं करेगा क्योंकि वर्तमान निर्देशिका उस नाम का उपयोग कर रही है।
फिन Finरूप नीलसन

6

हालांकि आपको सावधान रहना होगा। यह आपके इंट्रो में स्पष्ट रूप से नहीं कहा गया है, यदि आप केवल लेटीन भाषा में देख रहे हैं। कुछ शब्द निरर्थक या दूसरे अर्थ बन सकते हैं यदि आप उन्हें केवल अस्सी वर्णों के साथ पवित्र करते हैं।

कल्पना कीजिए कि आपके पास "फॉरेट पोएसी" (वन कविता) है, आपका स्वच्छता "फोर्ट-पॉसी" (मजबूत + कुछ अर्थहीन) दे सकता है

इससे भी बदतर अगर आपको चीनी पात्रों से निपटना पड़े।

"" Doing 沢 "आपका सिस्टम" --- "करना समाप्त कर सकता है जो कुछ समय बाद विफल हो जाता है और बहुत मददगार नहीं होता है। इसलिए यदि आप केवल फाइलों के साथ काम करते हैं, तो मैं उन्हें एक जेनेरिक चेन को कॉल करने के लिए प्रोत्साहित करूंगा, जिसे आप नियंत्रित करते हैं या पात्रों को वैसा ही रखना चाहते हैं। यूआरआई के लिए, उसी के बारे में।


6

क्यों न केवल "ऑसोपेन" को एक कोशिश / छोड़कर लपेटें और अंतर्निहित ओएस को यह बताएं कि क्या फ़ाइल वैध है?

यह बहुत कम काम की तरह लगता है और यह वैध नहीं है कि आप किस ओएस का उपयोग करते हैं।


5
क्या यह नाम को मान्य करता है? मेरा मतलब है, अगर ओएस खुश नहीं है, तो आपको अभी भी कुछ करने की ज़रूरत है, है ना?
jeromej

1
कुछ मामलों में, OS / भाषा चुपचाप आपके फ़ाइलनाम को एक वैकल्पिक रूप में बदल सकती है, लेकिन जब आप एक निर्देशिका लिस्टिंग करते हैं, तो आपको एक अलग नाम मिलेगा। और यह "जब मैं फ़ाइल को वहां लिखता हूं, तो यह" हो सकता है, लेकिन जब मैं फ़ाइल को इसके कुछ और "समस्या" के लिए देखता हूं। (मैं व्यवहार के बारे में बात कर रहा हूं जो मैंने वैक्स के बारे में सुना है ...)
केंट फ्रेड्रिक

इसके अलावा, "फ़ाइलनाम को कई ऑपरेटिंग सिस्टम पर मान्य होने की आवश्यकता है", जिसे आप osopenएक मशीन पर चलने के साथ पता नहीं लगा सकते हैं ।
लार्स

5

एक और मुद्दा जिसे अन्य टिप्पणियों ने अभी तक संबोधित नहीं किया है वह है रिक्त स्ट्रिंग, जो स्पष्ट रूप से एक वैध फ़ाइल नाम नहीं है। आप कई पात्रों को अलग करने से एक खाली स्ट्रिंग के साथ भी समाप्त कर सकते हैं।

विंडोज आरक्षित फ़ाइलनाम और डॉट्स के साथ मुद्दों के साथ क्या, सवाल का सबसे सुरक्षित जवाब "मैं कैसे मनमाने उपयोगकर्ता इनपुट से एक वैध फ़ाइल नाम को सामान्य कर सकता हूं?" है "कोशिश भी परेशान मत करो": यदि आप इसे से बचने के लिए कोई अन्य तरीका पा सकते हैं (जैसे फ़ाइल नाम के रूप में डेटाबेस से पूर्णांक प्राथमिक कुंजियों का उपयोग करना), तो ऐसा करें।

यदि आपको चाहिए, और आपको वास्तव में रिक्त स्थान और 'की अनुमति देने की आवश्यकता है।' नाम के हिस्से के रूप में फ़ाइल एक्सटेंशन के लिए, कुछ इस तरह आज़माएँ:

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

यहां तक ​​कि विशेष रूप से अप्रत्याशित OS पर भी इसकी गारंटी नहीं दी जा सकती - उदाहरण के लिए RISC OS रिक्त स्थान और उपयोग से नफरत करता है। ' एक निर्देशिका विभाजक के रूप में।


4

मुझे यहां अजगर-स्लीगाइज़ दृष्टिकोण पसंद आया लेकिन यह बहुत अलग था, जो कि वांछित नहीं था। इसलिए मैंने इसे इस तरह से s3 में एक साफ फ़ाइल नाम अपलोड करने के लिए अनुकूलित किया:

pip install python-slugify

उदाहरण कोड:

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

आउटपुट:

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

यह इतना विफल है, यह बिना विस्तार के फ़ाइल नाम के साथ काम करता है और यह केवल असुरक्षित वर्णों के फ़ाइल नाम के लिए भी काम करता है (परिणाम noneयहां है)।


1
मुझे यह पसंद है, पहिया को फिर से मजबूत न करें, पूरे Django ढांचे को आयात न करें यदि आपको इसकी आवश्यकता नहीं है, तो कोड को सीधे पेस्ट न करें यदि आप इसे भविष्य में बनाए रखने के लिए नहीं जा रहे हैं, और उत्पन्न स्ट्रिंग की कोशिश करता है सुरक्षित लोगों के समान अक्षरों से मेल खाता है, इसलिए नए स्ट्रिंग को पढ़ना आसान है।
विसेन्टेरिरे

1
डैश के बजाय अंडरस्कोर का उपयोग करने के लिए: name = slugify (s, विभाजक = '_')
vicenteherrera

3

अजगर 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)

क्या आप विवरण में अपना उत्तर बता सकते हैं?
Serenity

इसका वही जवाब सोफी गैग ने स्वीकार किया। लेकिन यह अजगर 3.6 पर काम करने के लिए संशोधित किया गया है
जीन-रॉबिन Tremblay

2

मुझे लगता है कि कई उत्तर हैं, लेकिन वे ज्यादातर नियमित अभिव्यक्ति या बाहरी मॉड्यूल पर भरोसा करते हैं, इसलिए मैं अपने जवाब में फेंकना चाहूंगा। एक शुद्ध अजगर फ़ंक्शन, कोई बाहरी मॉड्यूल की आवश्यकता नहीं, कोई नियमित अभिव्यक्ति का उपयोग नहीं किया गया। मेरा दृष्टिकोण अमान्य वर्णों को साफ करना नहीं है, बल्कि केवल वैध लोगों को अनुमति देना है।

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    

यदि आप चाहें, तो आप validcharsशुरुआत में अपने स्वयं के मान्य वर्णों को चर में जोड़ सकते हैं , जैसे कि आपके राष्ट्रीय पत्र जो अंग्रेजी वर्णमाला में मौजूद नहीं हैं। यह कुछ ऐसा है जो आप चाहते हैं या नहीं कर सकते हैं: कुछ फ़ाइल सिस्टम जो UTF-8 पर नहीं चलते हैं, फिर भी गैर-ASCIIs के साथ समस्या हो सकती है।

यह फ़ंक्शन एकल फ़ाइल नाम की वैधता के लिए परीक्षण करना है, इसलिए यह पथ विभाजकों को _ को अमान्य वर्ण मानकर _ के साथ बदल देगा। यदि आप इसे जोड़ना चाहते हैं, तो ifओएस पथ विभाजक को शामिल करना संशोधित करना तुच्छ है ।


1

इनमें से अधिकांश समाधान काम नहीं करते हैं।

'/ नमस्ते / दुनिया' -> 'helloworld'

'/ हेलोवर्ल्ड' / -> 'हेलोवर्ल्ड'

यह वह नहीं है जो आप आम तौर पर चाहते हैं, कहते हैं कि आप प्रत्येक लिंक के लिए HTML सहेज रहे हैं, आप एक अलग वेबपेज के लिए HTML को अधिलेखित करने जा रहे हैं।

मैं एक अचार का अचार बनाता हूँ जैसे:

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

2 उस संख्या का प्रतिनिधित्व करता है जिसे अगले फ़ाइलनाम में जोड़ा जाना चाहिए।

मैं हुकुम से हर बार फ़ाइल नाम देखता हूं। यदि यह नहीं है, तो मैं एक नया बनाता हूं, यदि आवश्यक हो तो अधिकतम संख्या को जोड़कर।


ध्यान दें, अगर helloworld1 का उपयोग कर रहे हैं, तो आपको यह भी जांचने की आवश्यकता है कि helloworld1 उपयोग में नहीं है और इतने पर नहीं है ..
robert King

1

ठीक नहीं है कि ओपी के लिए पूछ रहा था, लेकिन यह वह है जो मैं उपयोग करता हूं क्योंकि मुझे अद्वितीय और प्रतिवर्ती रूपांतरण चाहिए:

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

परिणाम "कुछ हद तक" पठनीय है, कम से कम एक sysadmin दृष्टिकोण से।


फ़ाइलों के नाम में कोई रिक्त स्थान नहीं है, इसके लिए एक आवरण:def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
स्पीडकोड 5

1

यदि आपको पैकेज स्थापित करने में कोई आपत्ति नहीं है, तो यह उपयोगी होनी चाहिए: https://pypi.org/project/pathvalidate/

से 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")

उत्पादन

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

मुझे यकीन है कि यह एक महान जवाब नहीं है, क्योंकि यह स्ट्रिंग को संशोधित कर रहा है, लेकिन यह ठीक काम करता है:

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

मैंने इसे "".join( x for x in s if (x.isalnum() or x in "._- "))इस पोस्ट टिप्पणियों पर पाया है
सर्जियोआराज़ो

0

अपडेट करें

इस 6 साल पुराने उत्तर में मरम्मत से परे सभी लिंक टूट गए।

इसके अलावा, मैं भी इसे इस तरह से नहीं करूंगा, बस base64असुरक्षित वर्णों को सांकेतिक शब्दों में बदलना या छोड़ दूंगा। पायथन 3 उदाहरण:

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

साथ base64आप सांकेतिक शब्दों में बदलना और डिकोड कर सकते हैं, तो आप मूल फ़ाइल का नाम फिर से प्राप्त कर सकते हैं।

लेकिन उपयोग के मामले के आधार पर आप एक यादृच्छिक फ़ाइल नाम उत्पन्न करने और मेटाडेटा को अलग फ़ाइल या 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'

मूल लिंकेज ANSWER :

bobcatपरियोजना एक अजगर मॉड्यूल है कि सिर्फ इस करता है शामिल हैं।

यह पूरी तरह से मजबूत नहीं है, इस पोस्ट और इस उत्तर को देखें

इसलिए, जैसा कि उल्लेख किया गया है: base64यदि पठनीयता मायने नहीं रखती है तो एन्कोडिंग शायद एक बेहतर विचार है।


सभी लिंक मृत। यार, कुछ तो करो।
शांतिप्रिय कोडर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.