2009-03-06 16 views
70

我有一個像「Foo:Bar」這樣的字符串,我想用它作爲文件名,但在Windows上,文件名中不允許使用「:」字符。如何從任意字符串中創建一個有效的Windows文件名?

有沒有一種將「Foo:Bar」變成「Foo- Bar」之類的方法?

+1

今天我做了同樣的事情。我沒有檢查出於某種原因,但無論如何找到了答案。 – 2009-03-06 22:44:04

回答

120

嘗試這樣:

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

編輯:

由於GetInvalidFileNameChars()將回到10或15個字符,最好使用StringBuilder,而不是一個簡單的字符串;原始版本將花費更長的時間並消耗更多的內存。

+0

S.I.P.GIFNC良好的呼叫。循環大概是我最終做的,但我並不是瘋狂地調用string.Replace在一個循環中 - 我希望有一個簡單*和*效率的內建。 – Ken 2009-03-09 17:07:38

+1

如果你願意,你可以使用StringBuilder,但如果名字很短,我想這不值得。你也可以創建自己的方法來創建一個char []並在一次迭代中替換所有錯誤的字符。 總是更好的保持簡單,除非它不起作用,否則瓶頸可能會變得更糟 – 2009-03-10 14:55:43

+0

我不知道c#,但是不可能使用帶有一組字符的remove()方法嗎?這組字符似乎是由GetInvalidFileNameChars()方便地提供的。 另外,現實地說,該循環會迭代多少次? 6通常,最多40個,如果該結構也返回非印刷的ascii,也許? 警告:該函數的msdn還提到您應該使用GetInvalidPathChars,因爲GIFNC不返回'\'或'/',這是無效的文件名字符。 – Pod 2009-09-09 11:04:02

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

但是「:」不是Windows的唯一非法字符。你還必須處理:

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

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

另外(在Windows上),「。」不能是文件名中唯一的字符(「。」,「..」,「...」等無效)。與命名文件時要小心,例如「‘:

echo "test" > .test. 

將生成一個名爲’。測試」

最後文件,如果你真的正確地做事情,也有一些special file names你需要注意。 在Windows不能創建命名的文件:

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. 
6

迭戈確實有正確的解決方案,但有一個非常小的失誤在那裏。正在使用的string.Replace的版本應該是string.Replace(char,char),不存在字符串.Replace(char,string)

我無法編輯答案,或者我只是做了微小的變化。

所以它應該是:

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

您可以用sed命令做到這一點:

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

這是不是更有效,但它更多的樂趣:)

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

我今天需要這樣做......在我的情況下,我需要將客戶名稱與最終.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)); 

如果將空格字符添加到無效數組中,您甚至可以將其替換爲空格。也許這不是最快的,但由於表現不是問題,我發現它優雅和可以理解。

乾杯!

1

清潔一點點我的代碼,使一個小的重構......我創建了字符串類型的擴展名:

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:'#'); 

而且你可以添加字符來代替.. fo R實施例你不想空格或逗號:

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

希望它可以幫助...

乾杯

5

這裏有迭戈的回答輕微扭動。

如果你不害怕Unicode,你可以通過用類似它們的有效Unicode符號替換無效字符來保留更高保真度。下面是我在最近的一個項目使用包括木材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; 
} 

由此產生的文件名一樣1⁄2」 spruce.txt,而不是1_2_ spruce.txt

是的,它確實有效:

Explorer sample

買者自負

我知道這個招我窩uld在NTFS上工作,但很驚訝地發現它也適用於FAT和FAT32分區。這是因爲long filenamesstored in Unicode,即使是as far back作爲Windows 95/NT。我在Win7,XP,甚至是一個基於Linux的路由器上進行了測試,結果顯示OK。不能說在DOSBox裏面是一樣的。

這就是說,在你堅持這個之前,考慮你是否真的需要額外的保真度。 Unicode的外觀可能會讓人們或老的程序混淆,例如較老的操作系統依靠codepages

6

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

下面是一個使用StringBuilderIndexOfAny散裝追加全效率的版本。它還返回原始字符串,而不是創建重複的字符串。

最後但並非最不重要的一點,它有一個switch語句,可以返回看起來很像的字符,您可以根據自己的意願自定義字符。查看Unicode.org's confusables lookup查看您可能擁有的選項,具體取決於字體。

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保留的名稱,因爲它是不明確的更換應該是什麼。

1

下面是使用Linq它使用Enumerable.Aggregate接受答案的一個版本:

string fileName = "something"; 

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

另一種簡單的解決方案:

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()); 
} 
相關問題