How to make a valid Windows filename from an arbitrary string?

后端 未结 14 931
伪装坚强ぢ
伪装坚强ぢ 2020-12-23 02:50

I\'ve got a string like \"Foo: Bar\" that I want to use as a filename, but on Windows the \":\" char isn\'t allowed in a filename.

Is there a method that will turn \

相关标签:
14条回答
  • 2020-12-23 03:08

    I needed to do this today... in my case, I needed to concatenate a customer name with the date and time for a final .kmz file. My final solution was this:

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

    You can even make it replace spaces if you add the space char to the invalid array.

    Maybe it's not the fastest, but as performance wasn't an issue, I found it elegant and understandable.

    Cheers!

    0 讨论(0)
  • 2020-12-23 03:10

    In case anyone wants an optimized version based on StringBuilder, use this. Includes rkagerer's trick as an option.

    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;
    }
    
    0 讨论(0)
  • 2020-12-23 03:12

    You can do this with a sed command:

     sed -e "
     s/[?()\[\]=+<>:;©®”,*|]/_/g
     s/"$'\t'"/ /g
     s/–/-/g
     s/\"/_/g
     s/[[:cntrl:]]/_/g"
    
    0 讨论(0)
  • 2020-12-23 03:14

    Here's a slight twist on Diego's answer.

    If you're not afraid of Unicode, you can retain a bit more fidelity by replacing the invalid characters with valid Unicode symbols that resemble them. Here's the code I used in a recent project involving lumber cutlists:

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

    This produces filenames like 1⁄2” spruce.txt instead of 1_2_ spruce.txt

    Yes, it really works:

    Explorer sample

    Caveat Emptor

    I knew this trick would work on NTFS but was surprised to find it also works on FAT and FAT32 partitions. That's because long filenames are stored in Unicode, even as far back as Windows 95/NT. I tested on Win7, XP, and even a Linux-based router and they showed up OK. Can't say the same for inside a DOSBox.

    That said, before you go nuts with this, consider whether you really need the extra fidelity. The Unicode look-alikes could confuse people or old programs, e.g. older OS's relying on codepages.

    0 讨论(0)
  • 2020-12-23 03:15

    A simple one line code:

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

    You can wrap it in an extension method if you want to reuse it.

    public static string ToValidFileName(this string fileName) => Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
    
    0 讨论(0)
  • 2020-12-23 03:17

    Diego does have the correct solution but there is one very small mistake in there. The version of string.Replace being used should be string.Replace(char, char), there isn't a string.Replace(char, string)

    I can't edit the answer or I would have just made the minor change.

    So it should be:

    string fileName = "something";
    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {
       fileName = fileName.Replace(c, '_');
    }
    
    0 讨论(0)
提交回复
热议问题