title: C# Sanitize File Name
body: <p>I recently have been moving a bunch of MP3s from various locations into a repository. I had been constructing the new file names using the ID3 tags (thanks, TagLib-Sharp!), and I noticed that I was getting a <code>System.NotSupportedException</code>: </p>
  
  <blockquote>
    <p><em>"The given path's format is not supported."</em></p>
  </blockquote>
  
  <p>This was generated by either <code>File.Copy()</code> or <code>Directory.CreateDirectory()</code>.</p>
  
  <p>It didn't take long to realize that my file names needed to be sanitized. So I did the obvious thing:</p>
  
  <pre><code>public static string SanitizePath_(string path, char replaceChar)
  {
      string dir = Path.GetDirectoryName(path);
      foreach (char c in Path.GetInvalidPathChars())
          dir = dir.Replace(c, replaceChar);
  
      string name = Path.GetFileName(path);
      foreach (char c in Path.GetInvalidFileNameChars())
          name = name.Replace(c, replaceChar);
  
      return dir + name;
  }
  </code></pre>
  
  <p>To my surprise, I continued to get exceptions. It turned out that ':' is not in the set of <code>Path.GetInvalidPathChars()</code>, because it is valid in a path root. I suppose that makes sense - but this has to be a pretty common problem. Does anyone have some short code that sanitizes a path? The most thorough I've come up with this, but it feels like it is probably overkill.</p>
  
  <pre><code>    // replaces invalid characters with replaceChar
      public static string SanitizePath(string path, char replaceChar)
      {
          // construct a list of characters that can't show up in filenames.
          // need to do this because ":" is not in InvalidPathChars
          if (_BadChars == null)
          {
              _BadChars = new List&lt;char&gt;(Path.GetInvalidFileNameChars());
              _BadChars.AddRange(Path.GetInvalidPathChars());
              _BadChars = Utility.GetUnique&lt;char&gt;(_BadChars);
          }
  
          // remove root
          string root = Path.GetPathRoot(path);
          path = path.Remove(0, root.Length);
  
          // split on the directory separator character. Need to do this
          // because the separator is not valid in a filename.
          List&lt;string&gt; parts = new List&lt;string&gt;(path.Split(new char[]{Path.DirectorySeparatorChar}));
  
          // check each part to make sure it is valid.
          for (int i = 0; i &lt; parts.Count; i++)
          {
              string part = parts[i];
              foreach (char c in _BadChars)
              {
                  part = part.Replace(c, replaceChar);
              }
              parts[i] = part;
          }
  
          return root + Utility.Join(parts, Path.DirectorySeparatorChar.ToString());
      }
  </code></pre>
  
  <p>Any improvements to make this function faster and less baroque would be much appreciated.</p>