如何从任意string中创build一个有效的Windows文件名?

我有一个像“Foo:Bar”这样的string,我想用它作为文件名,但在Windows上,文件名中不允许使用“:”字符。

有没有办法将“Foo:Bar”变成“Foo- Bar”之类的东西?

尝试这样的事情:

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

编辑:

由于GetInvalidFileNameChars()将返回10或15个字符,所以最好使用StringBuilder而不是简单的string。 原始版本将花费更长的时间并消耗更多的内存。

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

不过“:”并不是Windows的唯一非法字符。 你还必须处理:

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

这些包含在System.IO.Path.GetInvalidFileNameChars();

另外(在Windows上),“。” 不能是文件名中的唯一字符(“。”,“..”,“…”等无效)。 用“。”命名文件时要小心,例如:

 echo "test" > .test. 

将生成一个名为“.test”的文件

最后,如果你确实想要正确地做事情,那么你需要注意一些特殊的文件名 。 在Windows上,您不能创build名为的文件:

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

迭戈确实有正确的解决scheme,但在那里有一个非常小的错误。 正在使用的string.Replace的版本应该是string.Replace(char,char),没有一个string.Replace(char,string)

我不能编辑答案,或者我只是做了小小的改变。

所以它应该是:

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

如果有人想要基于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; } 

迭戈的答案略有不同。

如果你不害怕Unicode,你可以通过用类似Unicode的有效Unicode符号replace无效字符来保持更高的保真度。 这是我在最近一个涉及木材切割列表的项目中使用的代码:

 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示例

买者自负

我知道这个技巧可以在NTFS上运行,但是却惊讶地发现它也可以在FAT和FAT32分区上运行。 这是因为长文件名 以Unicode存储 ,即使早在Windows 95 / NT。 我在Win7,XP,甚至是基于Linux的路由器上进行了testing,结果显示OK。 不能说在DOSBox里面一样。

也就是说,在你坚持下去之前,考虑你是否真的需要额外的保真度。 Unicode的外观可能会让人们或老的程序混淆,例如老的操作系统依赖代码页 。

这是一个使用StringBuilderIndexOfAny的版本,可以批量追加以提高效率。 它也返回原始string,而不是创build一个重复的string。

最后但并非最不重要的一点,它有一个switch语句,可以返回类似于你想要的任何方式的字符。 查看Unicode.org的confusables查找 ,看看你可能有什么select,取决于字体。

 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等保留名称,因为不清楚replace应该是什么。

清理一点我的代码,并进行一些重构…我创build了一个stringtypes的扩展:

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

如果你想用“_”replace一个不同的字符,你可以使用:

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

你可以添加字符replace..例如,你不想要空格或逗号:

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

希望能帮助到你…

干杯

另一个简单的方法

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

我今天需要这样做…在我的情况下,我需要连接一个客户名称与最后一个.kmz文件的date和时间。 我最终的解决办法是

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

如果将空格char添加到无效数组中,您甚至可以将其replace为空格。

也许这不是最快的,但由于performance不是问题,我发现它优雅和可以理解。

干杯!

以下是使用Linq的接受答案的一个版本,它使用Enumerable.Aggregate

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

你可以用sed命令来做到这一点:

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