هذا هو أفضل طريقة للحصول على نسخة فريدة من نوعها من اسم الملف w/ الثعبان ؟

StackOverflow https://stackoverflow.com/questions/183480

  •  06-07-2019
  •  | 
  •  

سؤال

لا يزال 'الغوص في' Python و أريد التأكد من أنني لا تطل على شيء.كتبت السيناريو الذي يستخرج الملفات من عدة ملفات zip و يحفظ الملفات المستخرجة معا في دليل واحد.لمنع تكرار أسماء من كتب, كتبت هذه الوظيفة الصغيرة - و أنا أتساءل فقط إذا كان هناك طريقة أفضل للقيام بذلك ؟ وذلك بفضل!

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

أنا حقا هل تتطلب الملفات في دليل واحد ، ترقيم التكرارات هو بالتأكيد مقبولة في حالتي, أنا لا أبحث عن أكثر قوة الأسلوب (tho' أعتقد أن أي مؤشرات هي موضع ترحيب) ، ولكن فقط للتأكد من أن ما يحقق هذا هو الحصول على القيام به بالطريقة الصحيحة.

هل كانت مفيدة؟

المحلول

مسألة واحدة هي أن هناك حالة سباق في البرمجية أعلاه ، لأن هناك فجوة بين اختبار الوجود و إنشاء الملف.قد يكون هناك تداعيات أمنية إلى هذا (فكر في شخص ضار إدراج الارتباط الرمزي إلى حساسية الملف الذي أنها لن تكون قادرة على الكتابة ، ولكن البرنامج يعمل مع أعلى امتياز يمكن) هجمات مثل هؤلاء هم السبب في أشياء مثل نظام التشغيل.tempnam() يتم إهمال.

للحصول على من حوله ، فإن أفضل نهج هو في الواقع محاولة إنشاء الملف في مثل هذه الطريقة التي سوف تحصل على استثناء إذا فشل على النجاح يعود في الواقع فتح ملف الكائن.ويمكن أن يتم هذا مع انخفاض مستوى نظام التشغيل.فتح وظائف عن طريق تمرير كل من نظام التشغيل.O_CREAT و نظام التشغيل.O_EXCL الأعلام.فتحت مرة واحدة ، عودة الملف الفعلي (اختياريا اسم الملف) تقوم بإنشائها.على سبيل المثال ، قانون تعديل استخدام هذا النهج (العودة (ملف filename) tuple):

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.TemporaryFile و NamedTemporaryFile التي سبق و أيضا حذف تلقائيا من القرص عندما أغلقت.

نصائح أخرى

نعم، هذا هو استراتيجية جيدة لأسماء الملفات قابلة للقراءة ولكن فريدة من نوعها.

<القوي> تغيير واحد مهم : ليجب استبدال os.path.isfile مع os.path.lexists! كما هو مكتوب في الوقت الراهن، إذا كان هناك دليل اسمه /foo/bar.baz، فإن البرنامج يحاول الكتابة أنه مع ملف جديد (التي لن تعمل) ... منذ isfile فقط بالتحقق الملفات والدلائل لا . الشيكات lexists الدلائل، روابط الرمزية، الخ ... أساسا إذا كان هناك أي سبب أن اسم الملف لا يمكن أن تنشأ.

وتحرير: أعطىBrian إجابة أفضل، وهو أكثر أمانا وقوية من حيث شروط السباق

.

واثنين من التغييرات الصغيرة ...

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

ويمكنك الحصول على نتيجتين مع معنى متميزا، ومنحهم أسماء مختلفة.

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

وانها ليست أسرع أو بشكل ملحوظ أقصر. ولكن، إذا كنت ترغب في تغيير نمط الخاصة بك اسم الملف، ونمط على مكان واحد، وأسهل قليلا للعمل مع.

إذا كنت تريد أسماء قراءة هذا يبدو وكأنه حل جيد.
هناك الروتينية للعودة أسماء الملفات فريدة للمثل. الملفات المؤقتة ولكنها تنتج أسماء عشوائية تبحث طويلة.

وإذا كنت لا تبالي القراءة، 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)

وماذا عن

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

والوقت () إرجاع الوقت الحالي في مللي ثانية. جنبا إلى جنب مع اسم الملف الأصلي، انها فريدة من نوعها إلى حد ما حتى في الحالات مؤشرات معقدة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top