2008-10-08 16 views
14

अभी भी 'पाइथन में डाइविंग', और यह सुनिश्चित करना चाहते हैं कि मैं कुछ नहीं देख रहा हूं। मैंने एक स्क्रिप्ट लिखी है जो कई ज़िप फ़ाइलों से फ़ाइलों को निकालती है, और निकाली गई फ़ाइलों को एक निर्देशिका में एक साथ सहेजती है। डुप्लिकेट फ़ाइल नामों को ओवर-लिखित होने से रोकने के लिए, मैंने यह छोटा फ़ंक्शन लिखा - और मैं बस सोच रहा हूं कि ऐसा करने का बेहतर तरीका है या नहीं? धन्यवाद!क्या यह फ़ाइल नाम w/पायथन का अद्वितीय संस्करण प्राप्त करने का सबसे अच्छा तरीका है?

def unique_filename(file_name): 
counter = 1 
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext') 
while os.path.isfile(file_name): 
    file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1] 
    counter += 1 
return file_name 

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

उत्तर

22

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

इसके आस-पास पहुंचने के लिए, सबसे अच्छा तरीका यह है कि वास्तव में फ़ाइल को इस तरह से बनाने का प्रयास करें कि अगर यह विफल हो जाए तो आपको अपवाद मिलेगा, और सफलता पर, वास्तव में खुली फ़ाइल ऑब्जेक्ट को वापस कर दें। Os.O_CREAT और os.O_EXCL फ़्लैग दोनों को पार करके, निम्न स्तर os.open फ़ंक्शंस के साथ किया जा सकता है। एक बार खोले जाने के बाद, आपके द्वारा बनाई गई वास्तविक फ़ाइल (और वैकल्पिक रूप से फ़ाइल नाम) वापस करें।

def unique_file(file_name): 
    counter = 1 
    file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext') 
    while 1: 
     try: 
      fd = os.open(file_name, os.O_CREAT | os.O_EXCL | os.O_RDRW) 
      return os.fdopen(fd), file_name 
     except OSError: 
      pass 
     file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1] 
     counter += 1 

[संपादित करें] वास्तव में, एक बेहतर तरीका है, जो आप के लिए उपरोक्त मुद्दों को संभाल लेंगे, शायद है: उदाहरण के लिए, यहाँ अपने कोड इस दृष्टिकोण (एक (फ़ाइल लौटने, फ़ाइल का नाम) टपल) का उपयोग करने के लिए संशोधित है tempfile मॉड्यूल का उपयोग करने के लिए, हालांकि आप नामकरण पर कुछ नियंत्रण खो सकते हैं।यहाँ यह का उपयोग कर (एक समान अंतरफलक रखने) का एक उदाहरण है:

def unique_file(file_name): 
    dirname, filename = os.path.split(file_name) 
    prefix, suffix = os.path.splitext(filename) 

    fd, filename = tempfile.mkstemp(suffix, prefix+"_", dirname) 
    return os.fdopen(fd), filename 

>>> f, filename=unique_file('/home/some_dir/foo.txt') 
>>> print filename 
/home/some_dir/foo_z8f_2Z.txt 

इस दृष्टिकोण के साथ केवल नकारात्मक पक्ष यह है कि आप हमेशा उस में कुछ यादृच्छिक पात्रों के साथ एक फ़ाइल नाम मिल जाएगा, के रूप में वहाँ एक असंशोधित फ़ाइल बनाने के लिए कोई कोशिश नहीं की है (/home/some_dir/foo.txt) पहले। आप tempfile को भी देखना चाह सकते हैं। समकालीन फ़ाइल और नामांकित समकालीन फ़ाइल, जो उपर्युक्त करेगा और बंद होने पर स्वचालित रूप से डिस्क से भी हटा देगा।

+0

हां, यह करने के लिए The_Right_Way है। काश मैं खुद को संशोधित कर सकता हूं और अपना जवाब शीर्ष पर रख सकता हूं! –

+1

छोटे टाइपो: यह 'os.O_RDRW' की बजाय' os.O_RDWR' होना चाहिए – tremby

1

यदि आप पठनीय नाम चाहते हैं तो यह एक अच्छा समाधान जैसा दिखता है।
उदाहरण के लिए अद्वितीय फ़ाइल नाम वापस करने के लिए दिनचर्या हैं। अस्थायी फ़ाइलें लेकिन वे लंबे यादृच्छिक दिखने वाले नाम उत्पन्न करते हैं।

2

दो छोटे परिवर्तन ...

base_name, ext = os.path.splitext(file_name) 

आप दो परिणाम अलग अर्थ के साथ मिलता है, उन्हें अलग नाम दे।

file_name = "%s_%d%s" % (base_name, str(counter), ext) 

यह तेज़ या काफी छोटा नहीं है। लेकिन, जब आप अपना फ़ाइल नाम पैटर्न बदलना चाहते हैं, तो पैटर्न एक ही स्थान पर है, और साथ काम करने में थोड़ा आसान है।

6

हां, यह पठनीय लेकिन अद्वितीय फ़ाइल नामों के लिए एक अच्छी रणनीति है।

एक महत्वपूर्ण परिवर्तन: आपको os.path.lexists के साथ प्रतिस्थापित करना चाहिए! जैसा कि अभी लिखा गया है, अगर /foo/bar.baz नाम की एक निर्देशिका है, तो आपका प्रोग्राम नई फ़ाइल (जो काम नहीं करेगा) के साथ ओवरराइट करने का प्रयास करेगा ... क्योंकि isfile केवल फाइलों के लिए जांचता है और निर्देशिका नहीं । lexists निर्देशिकाओं, symlinks, आदि के लिए जांच ... मूल रूप से अगर कोई कारण है कि फ़ाइल नाम नहीं बनाया जा सका।

संपादित करें: @ ब्रायन ने एक बेहतर उत्तर दिया, जो दौड़ की स्थिति के मामले में अधिक सुरक्षित और मजबूत है।

1

यदि आपको पठनीयता की परवाह नहीं है, तो uuid.uuid4() आपका मित्र है।

import uuid 

def unique_filename(prefix=None, suffix=None): 
    fn = [] 
    if prefix: fn.extend([prefix, '-']) 
    fn.append(str(uuid.uuid4())) 
    if suffix: fn.extend(['.', suffix.lstrip('.')]) 
    return ''.join(fn) 
0

कैसे

के बारे में
def ensure_unique_filename(orig_file_path):  
    from time import time 
    import os 

    if os.path.lexists(orig_file_path): 
     name, ext = os.path.splitext(orig_file_path) 
     orig_file_path = name + str(time()).replace('.', '') + ext 

    return orig_file_path 

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

संबंधित मुद्दे

 संबंधित मुद्दे