How to make a valid Windows file name from an arbitrary string?

I have a line like "Foo: Bar" that I want to use as a file name, but on Windows ":" char is not specified in the file name.

Is there a way that will turn "Foo: Bar" into something like "Foo-Bar"?

+84
c # windows filenames
Mar 06 '09 at 22:05
source share
13 answers

Try something like this:

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

Edit:

Since GetInvalidFileNameChars() will return 10 or 15 characters, it is better to use StringBuilder instead of a simple string; the original version will take longer and consume more memory.

+142
Mar 06 '09 at 22:09
source share
 fileName = fileName.Replace(":", "-") 

However, ":" is not the only illegal character for Windows. You will also have to contact:

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

They are contained in System.IO.Path.GetInvalidFileNameChars ();

Also (on Windows), "." cannot be the only character in the file name (both ".", "..", "...", etc. are not valid). Be careful when naming files with "." , For example:

 echo "test" > .test. 

Generates a file named ".test"

Finally, if you really want to do something right, there are some special file names that you need to look at from behind. On Windows, you cannot create files with the name:

 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. 
+32
Mar 06 '09 at 22:14
source share

It is not more efficient, but it is more fun :)

  var fileName = "foo:bar"; var invalidChars = System.IO.Path.GetInvalidFileNameChars(); var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>()); 
+13
Nov 10 '11 at 16:16
source share

If someone wants an optimized version based on StringBuilder , use this. Includes the rkagerer 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; } 
+12
Aug 09 '14 at 10:56
source share

Diego has the right decision, but there is one very small mistake. The version of string.Replace is used as string.Replace (char, char), there is no string.Replace (char, string)

I cannot edit the answer, otherwise I would just make minor changes.

So this should be:

 string fileName = "something"; foreach (char c in System.IO.Path.GetInvalidFileNameChars()) { fileName = fileName.Replace(c, '_'); } 
+7
Sep 09 '09 at 7:49
source share

Here's a little twist on Diego’s response.

If you're not afraid of Unicode, you can be a little more faithful by replacing invalid characters with valid Unicode characters that resemble them. Here is the code I used in a recent project involving woodcarvers:

 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 creates file names such as 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 that it also works on FAT and FAT32 partitions. This is because long file names are stored in Unicode , even as far as possible as Windows 95 / NT. I tested on Win7, XP, and even on a Linux router, and they appeared normally. Can't say the same for DOSBox.

However, before you go crazy, think about whether you really need extra fidelity. Unicode views can confuse people or older programs, for example. Older OSs that rely on code pages .

+7
Aug 01 '14 at 8:09
source share

Here is the version of the accepted answer using Linq that uses Enumerable.Aggregate :

 string fileName = "something"; Path.GetInvalidFileNameChars() .Aggregate(fileName, (current, c) => current.Replace(c, '_')); 
+7
Dec 10 '15 at 17:48
source share

Here's a version using StringBuilder and IndexOfAny with volumetric addition for full efficiency. It also returns the original string, rather than creating a repeating string.

And last but not least, it has a switch statement that returns similar characters that you can configure in any way. Check out the Unicode.org confusables lookup to find out what options you may have, depending on the font.

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

He does not check availability . , .. or reserved names such as CON , because it is not clear what replacement should be.

+4
May 08 '15 at 14:20
source share

Clearing my code and introducing a little refactoring ... I created an extension for the string type:

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

Now it is easier to use with:

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

If you want to replace with a char other than "_", you can use:

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

And you can add characters to replace .. for example, you don't want spaces or commas:

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

Hope this helps ...

Greetings

+3
Aug 01 '13 at
source share

Another simple solution:

 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
Apr 05 '17 at 19:11
source share

I needed to do this today ... in my case, I needed to combine the client name with the date and time for the final .kmz file. My final decision was as follows:

  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 replace it with spaces if you add a char space to an invalid array.

This may not be the fastest, but since performance was not a problem, I found it elegant and straightforward.

Hurrah!

0
Aug 01 '13 at 10:25
source share

I needed a system that could not create collisions, so I could not match multiple characters to one. I ended up with:

 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
Aug 28 '19 at 19:57
source share

You can do this with the sed command:

  sed -e " s/[?()\[\]=+<>:;©®",*|]/_/g s/"$'\t'"/ /g s//-/g s/\"/_/g s/[[:cntrl:]]/_/g" 
-2
Dec 11 '10 at 1:00
source share



All Articles