سؤال

لدي سلسلة أريد استخدامها كاسم ملف، لذلك أريد إزالة كافة الأحرف التي لن يُسمح بها في أسماء الملفات، باستخدام Python.

أفضّل أن أكون صارمًا على خلاف ذلك، لذلك لنفترض أنني أريد الاحتفاظ فقط بالأحرف والأرقام ومجموعة صغيرة من الأحرف الأخرى مثل "_-.() ".ما هو الحل الأكثر أناقة؟

يجب أن يكون اسم الملف صالحًا على أنظمة تشغيل متعددة (Windows وLinux وMac OS) - فهو ملف MP3 في مكتبتي مع عنوان الأغنية كاسم ملف، ويتم مشاركته ونسخه احتياطيًا بين 3 أجهزة.

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

المحلول 7

وهذا هو الحل كنت في نهاية المطاف:

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)

والدعوة unicodedata.normalize يحل محل معلمة الأحرف مع ما يعادل غير مشدد، وهو أفضل من مجرد تجريد بها. بعد إزالة هذا أحرف فقط غير مسموح بها.

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

نصائح أخرى

ويمكنك إلقاء نظرة على بفك الإطار عن الكيفية التي خلق "سبيكة" من النص التعسفي. A سبيكة هي URL- وfilename- ودية.

ووتيلس النص جانغو تحدد وظيفة، 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))

وهناك أكثر من ذلك، ولكن تركت بها، لأنها لا تعالج slugification، لكن الهرب.

وهذا النهج القائمة البيضاء (أي السماح فقط للحرف موجودة في valid_chars) ستعمل إذا لم تكن هناك قيود على تنسيق الملفات أو مزيج من حرف صحيح غير قانونية (مثل "..")، على سبيل المثال، ما تقوله من شأنها أن تسمح اسم ملف اسمه ". النص" التي أعتقد أنها غير صالحة على ويندوز. ولما كان هذا هو النهج الأكثر بسيطة كنت في محاولة لإزالة المسافات من valid_chars وإلحاقها سلسلة صالحة معروفة في حالة الخطأ، فإن أي نهج آخر يجب أن تعرفه عن ما هو مسموح فيها للتعامل مع <لأ href = "HTTPS: //stackoverflow.com/questions/62771/how-check-if-given-string-is-legal-allowed-file-name-under-windows">Windows تسمية الملفات القيود وبالتالي تكون أكثر تعقيدا الكثير .

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

ويمكنك استخدام الفهم قائمة جنبا إلى جنب مع سلسلة طرق.

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

ما هو سبب لاستخدام السلاسل كأسماء الملف؟ إذا قراءة الإنسان ليست عاملا كنت أذهب مع وحدة base64 في التي يمكن أن تنتج سلاسل آمنة نظام الملفات. أنها لن تكون قابلة للقراءة ولكنك لن تضطر إلى التعامل مع حوادث الاصطدام وأنه هو عكسها.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

على تحديث : في. تغيير على أساس ماثيو تعليق

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

  • السلسلة عبارة عن أحرف غير صالحة (وتترك لك سلسلة فارغة)

  • ينتهي بك الأمر بسلسلة ذات معنى خاص ، على سبيل المثال ". أو ".."

  • على النوافذ، أسماء أجهزة معينة محجوزة.على سبيل المثال، لا يمكنك إنشاء ملف باسم "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

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

وهناك مشروع لطيفة على جيثب دعا -الثعبان slugify :

وتثبيت:

pip install python-slugify

وثم استخدم:

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

S.Lott وأجاب، يمكنك إلقاء نظرة في إطار جانغو للحصول على الكيفية التي تحويل سلسلة إلى اسم ملف صالح.

تم العثور على الإصدار الأحدث والتي تم تحديثها في تيلس / 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/ جانغو / فقاعة / الماجستير / جانغو / تيلس / text.py )

ضع في اعتبارك أنه لا توجد في الواقع أي قيود على أسماء الملفات على أنظمة يونكس بخلاف

  • قد لا يحتوي على \0
  • لا يجوز أن يحتوي على /

كل شيء آخر هو لعبة عادلة.

$ touch "
> even multiline
> haha
> ^[[31m red ^[[0m
> evil"
$ ls -la 
-rw-r--r--       0 Nov 17 23:39 ?even multiline?haha??[31m red ?[0m?evil
$ ls -lab
-rw-r--r--       0 Nov 17 23:39 \neven\ multiline\nhaha\n\033[31m\ red\ \033[0m\nevil
$ perl -e 'for my $i ( glob(q{./*even*}) ){ print $i; } '
./
even multiline
haha
 red 
evil

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

للترفيه، ضع حرف BEL في اسم الدليل وشاهد المتعة التي تنتج عندما تقوم بإدخال القرص المضغوط فيه؛)

في سطر واحد:

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

ويمكنك أيضا وضع '_' الطابع لجعله أكثر قابلية للقراءة (في حالة slashs استبدال، على سبيل المثال)

هل يمكن استخدام الأسلوب re.sub () ليحل محل أي شيء لا "filelike". ولكن في الواقع، كل حرف يمكن أن يكون صحيحا. لذلك لا توجد وظائف قبل البناء (أعتقد)، لانجاز ذلك.

import re

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

هل يؤدي إلى filehandle إلى /tmp/filename.txt.

>>> 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'، 'يخدع'، الخ).

وعلى الرغم من أن عليك أن تكون حذرا. لا يقال بوضوح في مقدمة الخاص بك، إذا كنت تبحث فقط في اللغة LATINE. بعض الكلمات يمكن أن تصبح بلا معنى أو معنى آخر إذا كنت تطهير لهم أحرف ASCII فقط.

وتخيل لديك "فوريه POESIE" (الشعر الغابات)، الإبراء الخاص بك قد تعطي "الحصن-posie" (قوي + شيء لا معنى له)

والأسوأ من ذلك إذا كان لديك للتعامل مع الحروف الصينية.

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

لماذا لا مجرد التفاف على "osopen" مع محاولة / باستثناء والسماح للOS الأساسي فرز ما إذا كان الملف صالح؟

وهذا يبدو وكأنه أقل بكثير العمل وغير سارية المفعول بغض النظر عن نظام التشغيل الذي تستخدمه.

وثمة مسألة أخرى أن تعليقات أخرى لم تعالج حتى الآن هي السلسلة الفارغة، التي من الواضح انها لا اسم ملف صالح. يمكنك أيضا في نهاية المطاف مع سلسلة فارغة من تجريد أحرف كثيرة جدا.

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

إذا يجب عليك، وكنت حقا بحاجة للسماح المساحات وملحقات الملف كجزء من الاسم، في محاولة شيء من هذا القبيل '.':

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

وحتى هذا لا يمكن ضمان حق خصوصا على أنظمة التشغيل غير متوقع - على سبيل المثال RISC OS يكره المساحات ويستخدم كفاصل الدليل

''.

وأنا أحب نهج الثعبان slugify هنا ولكن تم تجريد النقاط بعيدا أيضا الذي لم يكن المطلوب. لذلك أنا أمثل ذلك لتحميل اسم نظيفة ل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 نتيجة هنا).

ومعظم هذه الحلول لا تعمل.

و'/ مرحبا / العالم "->" helloworld'

و'/ helloworld' / -> 'helloworld'

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

وأنا مخلل على ديكت مثل:

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

و2 يمثل الرقم الذي يجب إلحاق اسم المقبل.

وأقدرك اسم الملف في كل مرة من ديكت. إذا لم يكن هناك، وإنشاء واحدة جديدة، إلحاق أقصى عدد إذا لزم الأمر.

وليس بالضبط ما OP كان يسأل عن ولكن هذا هو ما يمكنني استخدام لأنني بحاجة التحويلات الفريدة وعكسها:

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

والنتيجة هي "نوعا ما" للقراءة، على الأقل من وجهة نظر مسؤول النظام من الرأي.

وأنا متأكد من أن هذا ليس الجواب كبيرة، لأنه يعدل سلسلة حلقات انها قد انتهت، ولكن يبدو أن العمل على ما يرام:

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

تحديث

جميع الروابط معطلة ولا يمكن إصلاحها في هذه الإجابة البالغة من العمر 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 يمكنك التشفير وفك التشفير، حتى تتمكن من استرداد اسم الملف الأصلي مرة أخرى.

ولكن اعتمادًا على حالة الاستخدام، قد يكون من الأفضل إنشاء اسم ملف عشوائي وتخزين البيانات الوصفية في ملف منفصل أو قاعدة بيانات.

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'

الجواب الأصلي لينكروتن:

ال bobcat يحتوي المشروع على وحدة بايثون التي تقوم بذلك فقط.

انها ليست قوية تماما، انظر هذا بريد وهذا رد.

لذلك كما لاحظت: base64 ربما يكون الترميز فكرة أفضل إذا لم تكن سهولة القراءة مهمة.

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

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 قد لا تزال لديها مشاكل مع حرف غير ASCII

وهذه هي وظيفة لاختبار واحد اسم ملف صحة، لذلك سوف تحل محل فصل المسار مع _ اعتبارهم حرف غير صالح. إذا كنت ترغب في إضافة ل، فمن تافهة لتعديل if لتشمل فاصل مسار نظام التشغيل.

الإجابة معدلة لبيثون 3.6

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top