كيفية إنشاء اسم ملف Windows صالح من سلسلة عشوائية؟

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

  •  05-07-2019
  •  | 
  •  

سؤال

لدي سلسلة مثل "Foo:Bar" الذي أرغب في استخدامه كاسم ملف، ولكن في نظام التشغيل Windows، لا يُسمح بـ "char" في اسم الملف.

هل هناك طريقة من شأنها تحويل "Foo:Bar" إلى شيء مثل "Foo-Bar"؟

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

المحلول

جرب شيئًا مثل هذا:

string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
   fileName = fileName.Replace(c, '_');
}

يحرر:

منذ GetInvalidFileNameChars() سيرجع 10 أو 15 حرفًا، فمن الأفضل استخدام a StringBuilder بدلاً من سلسلة بسيطة؛سيستغرق الإصدار الأصلي وقتًا أطول ويستهلك المزيد من الذاكرة.

نصائح أخرى

fileName = fileName.Replace(":", "-") 

ومع ذلك، فإن ":" ليس الحرف الوحيد غير القانوني لنظام التشغيل Windows.سيكون عليك أيضًا التعامل مع:

/, \, :, *, ?, ", <, > and |

هذه موجودة في System.IO.Path.GetInvalidFileNameChars();

أيضا (على النوافذ) ، ". لا يمكن أن يكون الشخصية الوحيدة في اسم الملف (كلاهما "." ، ".." ، "..." ، وهكذا غير صالح).كن حذرًا عند تسمية الملفات باستخدام "."، على سبيل المثال:

echo "test" > .test.

سيتم إنشاء ملف باسم ".test"

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

CON, PRN, AUX, CLOCK$, NUL
COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9
LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.

وهذه ليست أكثر كفاءة، لكنها أكثر متعة:)

    var fileName = "foo:bar";
    var invalidChars = System.IO.Path.GetInvalidFileNameChars();
    var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());

في حالة أي شخص يريد نسخة محسنة على أساس StringBuilder، استخدم هذا. يتضمن خدعة rkagerer باعتبارها الخيار.

static char[] _invalids;

/// <summary>Replaces characters in <c>text</c> that are not allowed in 
/// file names with the specified replacement character.</summary>
/// <param name="text">Text to make into a valid filename. The same string is returned if it is valid already.</param>
/// <param name="replacement">Replacement character, or null to simply remove bad characters.</param>
/// <param name="fancy">Whether to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
/// <returns>A string that can be used as a filename. If the output string would otherwise be empty, returns "_".</returns>
public static string MakeValidFileName(string text, char? replacement = '_', bool fancy = true)
{
    StringBuilder sb = new StringBuilder(text.Length);
    var invalids = _invalids ?? (_invalids = Path.GetInvalidFileNameChars());
    bool changed = false;
    for (int i = 0; i < text.Length; i++) {
        char c = text[i];
        if (invalids.Contains(c)) {
            changed = true;
            var repl = replacement ?? '\0';
            if (fancy) {
                if (c == '"')       repl = '”'; // U+201D right double quotation mark
                else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
                else if (c == '/')  repl = '⁄'; // U+2044 fraction slash
            }
            if (repl != '\0')
                sb.Append(repl);
        } else
            sb.Append(c);
    }
    if (sb.Length == 0)
        return "_";
    return changed ? sb.ToString() : text;
}

ودييغو لديها الحل الصحيح ولكن هناك خطأ واحد صغير جدا هناك. إصدار string.Replace المستخدمة يجب أن تكون string.Replace (شار، تشار)، ليس هناك string.Replace (شار، سلسلة)

وأنا لا يمكن تحرير الجواب أو أنا من شأنها أن تجعل مجرد تغيير طفيف.

وهكذا ينبغي أن يكون:

string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
   fileName = fileName.Replace(c, '_');
}

إليك تطور طفيف في إجابة دييغو.

إذا لم تكن خائفًا من Unicode، فيمكنك الاحتفاظ بقدر أكبر من الدقة عن طريق استبدال الأحرف غير الصالحة برموز Unicode صالحة تشبهها.إليك الكود الذي استخدمته في مشروع حديث يتضمن قوائم قطع الخشب:

static string MakeValidFilename(string text) {
  text = text.Replace('\'', '’'); // U+2019 right single quotation mark
  text = text.Replace('"',  '”'); // U+201D right double quotation mark
  text = text.Replace('/', '⁄');  // U+2044 fraction slash
  foreach (char c in System.IO.Path.GetInvalidFileNameChars()) {
    text = text.Replace(c, '_');
  }
  return text;
}

وهذا ينتج أسماء الملفات مثل 1⁄2” spruce.txt بدلاً من 1_2_ spruce.txt

نعم، إنه يعمل حقًا:

Explorer sample

مسؤولية المشتري

كنت أعلم أن هذه الخدعة ستعمل على نظام NTFS ولكني فوجئت عندما وجدت أنها تعمل أيضًا على أقسام FAT وFAT32.ذلك بسبب أسماء الملفات الطويلة نكون المخزنة في يونيكود, ، حتى بقدر ما يعود مثل ويندوز 95/NT.لقد قمت بالاختبار على أنظمة Win7 وXP وحتى جهاز توجيه يعمل بنظام التشغيل Linux وقد ظهرت النتائج على ما يرام.لا أستطيع قول الشيء نفسه داخل DOSBox.

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

وفيما يلي نسخة من الجواب المقبول استخدام Linq الذي يستخدم <لأ href = "https://msdn.microsoft.com/en-us/library/bb549218(v=vs.110).aspx" يختلط = "noreferrer "> Enumerable.Aggregate :

string fileName = "something";

Path.GetInvalidFileNameChars()
    .Aggregate(fileName, (current, c) => current.Replace(c, '_'));

وهنا نسخة يستخدم StringBuilder وIndexOfAny مع معظم إلحاق لالكفاءة الكاملة. فإنها ترجع أيضا السلسلة الأصلية بدلا من إنشاء سلسلة مكررة.

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

public static string GetSafeFilename(string arbitraryString)
{
    var invalidChars = System.IO.Path.GetInvalidFileNameChars();
    var replaceIndex = arbitraryString.IndexOfAny(invalidChars, 0);
    if (replaceIndex == -1) return arbitraryString;

    var r = new StringBuilder();
    var i = 0;

    do
    {
        r.Append(arbitraryString, i, replaceIndex - i);

        switch (arbitraryString[replaceIndex])
        {
            case '"':
                r.Append("''");
                break;
            case '<':
                r.Append('\u02c2'); // '˂' (modifier letter left arrowhead)
                break;
            case '>':
                r.Append('\u02c3'); // '˃' (modifier letter right arrowhead)
                break;
            case '|':
                r.Append('\u2223'); // '∣' (divides)
                break;
            case ':':
                r.Append('-');
                break;
            case '*':
                r.Append('\u2217'); // '∗' (asterisk operator)
                break;
            case '\\':
            case '/':
                r.Append('\u2044'); // '⁄' (fraction slash)
                break;
            case '\0':
            case '\f':
            case '?':
                break;
            case '\t':
            case '\n':
            case '\r':
            case '\v':
                r.Append(' ');
                break;
            default:
                r.Append('_');
                break;
        }

        i = replaceIndex + 1;
        replaceIndex = arbitraryString.IndexOfAny(invalidChars, i);
    } while (replaceIndex != -1);

    r.Append(arbitraryString, i, arbitraryString.Length - i);

    return r.ToString();
}

وأنها لا تحقق ل.، ..، أو الأسماء المحجوزة مثل CON لأنه ليس من الواضح ما ينبغي أن تكون بديلا.

والحل بسيط آخر:

private string MakeValidFileName(string original, char replacementChar = '_')
{
  var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars());
  return new string(original.Select(c => invalidChars.Contains(c) ? replacementChar : c).ToArray());
}

وتنظيف قليلا قانون بلدي وجعل القليل من إعادة بيع ديون ... أنا خلقت امتدادا لنوع السلسلة:

public static string ToValidFileName(this string s, char replaceChar = '_', char[] includeChars = null)
{
  var invalid = Path.GetInvalidFileNameChars();
  if (includeChars != null) invalid = invalid.Union(includeChars).ToArray();
  return string.Join(string.Empty, s.ToCharArray().Select(o => o.In(invalid) ? replaceChar : o));
}

والآن حان أسهل للاستخدام مع:

var name = "Any string you want using ? / \ or even +.zip";
var validFileName = name.ToValidFileName();

إذا كنت تريد استبدال مع شار مختلفة من "_" يمكنك استخدام:

var validFileName = name.ToValidFileName(replaceChar:'#');

ويمكنك إضافة حرف ليحل محل .. على سبيل المثال كنت لا تريد مسافات أو فواصل:

var validFileName = name.ToValidFileName(includeChars: new [] { ' ', ',' });

ونأمل أن يساعد ...

وهتاف

وكنت بحاجة للقيام بذلك اليوم ... في حالتي، أنا في حاجة لسلسلة اسم العميل مع التاريخ والوقت للملف kmz لالنهائي. وكان بلدي الحل النهائي هذا:

 string name = "Whatever name with valid/invalid chars";
 char[] invalid = System.IO.Path.GetInvalidFileNameChars();
 string validFileName = string.Join(string.Empty,
                            string.Format("{0}.{1:G}.kmz", name, DateTime.Now)
                            .ToCharArray().Select(o => o.In(invalid) ? '_' : o));

ويمكنك حتى أن يحل محل مسافات إذا قمت بإضافة حرف الفضاء إلى مجموعة غير صالحة.

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

وهتاف!

ويمكنك القيام بذلك مع أمر sed:

 sed -e "
 s/[?()\[\]=+<>:;©®”,*|]/_/g
 s/"$'\t'"/ /g
 s/–/-/g
 s/\"/_/g
 s/[[:cntrl:]]/_/g"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top