System.IO.Path.GetInvalidFileNameChars() has all the invalid characters.
Here's a sample method:
public static string SanitizeFileName
(
string fileName,
char replacementChar = '_'
)
{
HashSet<char> blocked = new(System.IO.Path.GetInvalidFileNameChars());
char[] output = fileName.ToCharArray();
for (int i = 0, ln = output.Length; i < ln; i++)
{
if (blocked.Contains(output[i]))
{
output[i] = replacementChar;
}
}
return new String(output);
}
As pointed out by pixel, this would translate
my\\\file\\\\\\\\\\name
into
my___file__________name
... which may be unattractive.
If this function is something you might call in a batch or if you'd like the flexibility to replace consecutive invalid characters with a single replacement, you may want something like this:
public class FileNameSanitizer
{
// cache the blocked characters
private HashSet<char> _blocked = new(System.IO.Path.GetInvalidFileNameChars());
/// <summary>Replace individual invalid characters one-for-one.</summary>
public string Sanitize
(
string fileName,
char replacementChar = '_'
)
{
char[] output = fileName.ToCharArray();
for (int i = 0, ln = output.Length; i < ln; i++)
{
if (_blocked.Contains(output[i]))
{
output[i] = replacementChar;
}
}
return new String(output);
}
/// <summary>Replace consecutive invalid characters with a single replacement character.</summary>
public string SanitizeCondensed
(
string fileName,
char replacementChar = '_'
)
{
char[] output = fileName.ToCharArray();
char current;
bool replaced = false;
int i = 0, x = 0, ln = output.Length;
for (; i < ln; i++)
{
current = output[i];
if (_blocked.Contains(current))
{
if (!replaced)
{
output[x++] = replacementChar;
}
replaced = true;
}
else
{
output[x++] = current;
replaced = false;
}
}
return new String(output, 0, x);
}
}