मनमानी स्ट्रिंग से एक वैध विंडोज फ़ाइल नाम कैसे बनाया जाए?


97

मुझे "फू: बार" जैसा एक स्ट्रिंग मिला है जिसे मैं एक फ़ाइल नाम के रूप में उपयोग करना चाहता हूं, लेकिन विंडोज पर ":" चार का नाम एक फ़ाइलनाम में अनुमति नहीं है।

क्या कोई विधि है जो "फू: बार" को "फू-बार" की तरह बदल देगी?


1
मैंने आज भी यही किया। मैंने किसी कारण से SO की जाँच नहीं की, लेकिन फिर भी उत्तर मिला।
आरोन स्मिथ

जवाबों:


153

कुछ इस तरह की कोशिश करो:

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

संपादित करें:

चूंकि GetInvalidFileNameChars()10 या 15 वर्ण वापस आएंगे, इसलिए StringBuilderएक साधारण स्ट्रिंग के बजाय इसका उपयोग करना बेहतर है ; मूल संस्करण अधिक समय लगेगा और अधिक मेमोरी का उपभोग करेगा।


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

2
InvalidFileNameChars = new char [] "", '<', '>', '|', '|', '\ 0', '\ x0001', '\ x0002', '\ x0003', '\ x0004', '\ " x0005 ',' \ x0006 ',' \ 'a,' \ b ',' \ t ',' \ n ',' \ v ',' \ f ',' \ r ',' \ x000e ',' \ ' x000f ',' \ x0010 ',' \ x0011 ',' \ x0012 ',' \ x0013 ',' \ x0014 ',' \ x0015 ',' \ x0016 ',' \ x0017 ',' \ x0018 ',' \ _ x0019 ',' \ x001a ',' \ x001b ',' \ x001c ',' \ x001d ',' \ x001e ',' \ x001f ',': ',' * ','? ',' \\ ',? '/'};
डिएगो जेंसिक

9
स्ट्रिंग में 2+ विभिन्न अमान्य वर्ण होने की संभावना इतनी कम है कि स्ट्रिंग के प्रदर्शन के बारे में देखभाल की जाती है। प्लेस () बेकार है।
सर्ज वुटियर

1
शानदार समाधान, एक तरफ दिलचस्प, पुनर्जीवन ने इस लाइनक संस्करण का सुझाव दिया: फ़ाइलनाम = System.IO.Path.GetInvalidFileNameChars ()। एकत्र करना (फ़ाइल का नाम, (वर्तमान, c) => वर्तमान .eplace (c, '_')); मुझे आश्चर्य है कि अगर वहां कोई संभावित प्रदर्शन में सुधार हो। मैंने पठनीयता के उद्देश्यों के लिए मूल रखा है क्योंकि प्रदर्शन मेरी सबसे बड़ी चिंता नहीं है। लेकिन अगर किसी को दिलचस्पी है, तो बेंचमार्किंग के लायक हो सकता है
chrispepper1989

1
@AndyM की कोई आवश्यकता नहीं है। file.name.txt.pdfएक वैध पीडीएफ है। विंडोज .एक्सटेंशन के लिए केवल आखिरी पढ़ता है ।
डिएगो जानिक

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

हालांकि ":" विंडोज के लिए एकमात्र अवैध चरित्र नहीं है। आपको भी संभालना होगा:

/, \, :, *, ?, ", <, > 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.

3
मुझे आरक्षित नामों के बारे में कभी नहीं पता था। हालांकि समझ में आता है
ग्रेग डीन

4
इसके अलावा, इसके लायक क्या है, आप इन आरक्षित नामों में से एक के साथ शुरू होने वाला फ़ाइल नाम नहीं बना सकते हैं, उसके बाद एक दशमलव। यानी con.air.avi
जॉन कॉनराड

".foo" एक वैध फ़ाइल नाम है। "कांग्रेस" फ़ाइल नाम के बारे में पता नहीं था - यह किस लिए है?
विन्यासकर्ता

उसको खरोंचो। कॉन कंसोल के लिए है।
विन्यासकर्ता

धन्यवाद विन्यासकर्ता; मैंने जवाब अपडेट किया है, आप सही हैं ".foo" वैध है; हालाँकि ".foo।" संभव है, अवांछित परिणाम की ओर जाता है। अपडेट किया गया।
फिल प्राइस

13

यह अधिक कुशल नहीं है, लेकिन यह अधिक मजेदार है :)

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

12

यदि कोई इसके आधार पर अनुकूलित संस्करण चाहता है 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;
}

अच्छा और पठनीय कोड के लिए +1। बग को पढ़ने और नोटिस करने में बहुत आसान बनाता है: P .. इस फ़ंक्शन को हमेशा मूल स्ट्रिंग लौटना चाहिए क्योंकि परिवर्तित कभी भी सच नहीं होगा।
एर्टि-क्रिस इल्मा

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

8

यहाँ Linqजो उपयोग करता है स्वीकृत उत्तर का एक संस्करण है Enumerable.Aggregate:

string fileName = "something";

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

7

डिएगो के पास सही समाधान है लेकिन वहां एक बहुत छोटी गलती है। String .eplace का उपयोग किया जा रहा है string.eplace (char, char) का संस्करण होना चाहिए, एक string नहीं है। रीप्ले (char, string)

मैं उत्तर को संपादित नहीं कर सकता या मैंने केवल मामूली बदलाव किया होगा।

तो यह होना चाहिए:

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

7

यहाँ डिएगो के उत्तर पर एक हल्का मोड़ है।

यदि आप यूनिकोड से डरते नहीं हैं, तो आप अमान्य यूनिकोड प्रतीकों के साथ अमान्य वर्णों को प्रतिस्थापित करके थोड़ी अधिक निष्ठा बनाए रख सकते हैं। यहाँ हाल ही में एक परियोजना का उपयोग किया गया है जिसमें लंबर कटलिस्ट शामिल हैं:

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बजाय filenames पैदा करता है1_2_ spruce.txt

हाँ, यह वास्तव में काम करता है:

एक्सप्लोरर नमूना

कैविएट एम्प्टर

मुझे पता था कि यह ट्रिक NTFS पर काम करेगी, लेकिन यह जानकर आश्चर्य हुआ कि यह FAT और FAT32 विभाजन पर भी काम करती है। ऐसा इसलिए है क्योंकि लंबे फाइलनाम यूनिकोड में संग्रहीत हैं , यहां तक कि विंडोज 95 / एनटी के रूप में भी। मैंने Win7, XP और यहां तक ​​कि एक लिनक्स-आधारित राउटर पर परीक्षण किया और उन्होंने ओके दिखाया। DOSBox के अंदर के लिए समान नहीं कह सकते।

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


5

यहां एक संस्करण है जो पूर्ण दक्षता के लिए उपयोग करता है 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है क्योंकि यह स्पष्ट नहीं है कि क्या प्रतिस्थापन होना चाहिए।


3

मेरे कोड को थोड़ा साफ करना और थोड़ा रिफलेक्ट करना ... मैंने स्ट्रिंग प्रकार के लिए एक एक्सटेंशन बनाया:

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 [] { ' ', ',' });

आशा करता हूँ की ये काम करेगा...

चियर्स


3

एक और सरल उपाय:

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());
}

3

एक सरल एक लाइन कोड:

var validFileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));

यदि आप इसे पुन: उपयोग करना चाहते हैं, तो आप इसे एक्सटेंशन विधि में लपेट सकते हैं।

public static string ToValidFileName(this string fileName) => Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));

1

मुझे एक ऐसी प्रणाली की आवश्यकता थी जो टकराव पैदा न कर सके इसलिए मैं एक से अधिक वर्णों को मैप नहीं कर सकता था। मैं इसके साथ समाप्त हुआ:

public static class Extension
{
    /// <summary>
    /// Characters allowed in a file name. Note that curly braces don't show up here
    /// becausee they are used for escaping invalid characters.
    /// </summary>
    private static readonly HashSet<char> CleanFileNameChars = new HashSet<char>
    {
        ' ', '!', '#', '$', '%', '&', '\'', '(', ')', '+', ',', '-', '.',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '=', '@',
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        '[', ']', '^', '_', '`',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    };

    /// <summary>
    /// Creates a clean file name from one that may contain invalid characters in 
    /// a way that will not collide.
    /// </summary>
    /// <param name="dirtyFileName">
    /// The file name that may contain invalid filename characters.
    /// </param>
    /// <returns>
    /// A file name that does not contain invalid filename characters.
    /// </returns>
    /// <remarks>
    /// <para>
    /// Escapes invalid characters by converting their ASCII values to hexadecimal
    /// and wrapping that value in curly braces. Curly braces are escaped by doubling
    /// them, for example '{' => "{{".
    /// </para>
    /// <para>
    /// Note that although NTFS allows unicode characters in file names, this
    /// method does not.
    /// </para>
    /// </remarks>
    public static string CleanFileName(this string dirtyFileName)
    {
        string EscapeHexString(char c) =>
            "{" + (c > 255 ? $"{(uint)c:X4}" : $"{(uint)c:X2}") + "}";

        return string.Join(string.Empty,
                           dirtyFileName.Select(
                               c =>
                                   c == '{' ? "{{" :
                                   c == '}' ? "}}" :
                                   CleanFileNameChars.Contains(c) ? $"{c}" :
                                   EscapeHexString(c)));
    }
}

0

मुझे आज ऐसा करने की आवश्यकता थी ... मेरे मामले में, मुझे एक अंतिम .km फ़ाइल के लिए दिनांक और समय के साथ एक ग्राहक नाम को संक्षिप्त करने की आवश्यकता थी। मेरा अंतिम समाधान यह था:

 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));

यदि आप रिक्त स्थान को अवैध सरणी में जोड़ते हैं, तो आप इसे रिक्त स्थान भी बदल सकते हैं।

शायद यह सबसे तेज़ नहीं है, लेकिन जैसा कि प्रदर्शन एक मुद्दा नहीं था, मैंने इसे सुरुचिपूर्ण और समझने योग्य पाया।

चीयर्स!


-2

आप यह एक sedआदेश के साथ कर सकते हैं :

 sed -e "
 s/[?()\[\]=+<>:;©®”,*|]/_/g
 s/"$'\t'"/ /g
 s/–/-/g
 s/\"/_/g
 s/[[:cntrl:]]/_/g"

यह भी एक और अधिक जटिल लेकिन संबंधित प्रश्न देखें: stackoverflow.com/questions/4413427/…
DW

इसे बैश के बजाय C # में करने की आवश्यकता क्यों है? मैं अब मूल प्रश्न पर C # का टैग देखता हूं, लेकिन क्यों?
डीडब्ल्यू

1
मुझे पता है, ठीक है, क्यों न केवल C # आवेदन से बैश तक खोल दिया जाए जो इसे पूरा करने के लिए स्थापित नहीं किया जा सकता है?
पीटर रिची
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.