102

In my application the user can enter a filename. Before processing I'd like to check if the input String is a valid filename on Windows Vista.

Whats the easiest way to do that?

By valid I'm reffering to legal and non-existing

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
RoflcoptrException
  • 51,941
  • 35
  • 152
  • 200
  • When you say valid filename, are you referring to the fact that the file exists, or are you asking if the name would be allowed by the operation system? – Jesse McCulloch Jan 10 '11 at 19:09
  • 4
    Valid as in (a) existing, (b) legal, or (c) legal and non-existing? – Anthony Pegram Jan 10 '11 at 19:09
  • Sry i should have clarified that. It should be (c) leagl and non-existing – RoflcoptrException Jan 10 '11 at 19:09
  • 4
    @roflcopter: You must simply handle errors while creating the file. Any filesystem operation that involves an existence check is already potentially out-of-date by the time it returns, since the filesystem is a global shared resource. – Ben Voigt Jan 10 '11 at 19:15
  • @Ben Voigt: I think this should work, but I always thought Exception Handling shouldn't be used to model the normal control flow. – RoflcoptrException Jan 10 '11 at 19:18
  • 1
    @Roflcoptr: a file with the exact same name being created between the moment you check validity and/or existence, and the moment when you actually attempt to create it is not to be regarded normal program flow. You *should* verify that the file does not exist, but when you actually do create the file, that knowledge is already old and should be considered only an educated guess. It is likely to still be true, but it is not *guaranteed*. – Fredrik Mörk Jan 10 '11 at 19:20
  • @Roflcoptr: I agree, but Microsoft in their infinite wisdom didn't provide a `File.TryOpen` method so you're stuck handling exceptions. (Or use the Win32 API directly, `CreateFile` returns a failure code you can test for without using exceptions) – Ben Voigt Jan 10 '11 at 19:22
  • possible duplicate of [In C# check that filename is \*possibly\* valid (not that it exists)](http://stackoverflow.com/questions/422090/in-c-sharp-check-that-filename-is-possibly-valid-not-that-it-exists) – nawfal Jun 05 '13 at 11:41
  • Duplicate of https://stackoverflow.com/a/62888/8322623 Please note that every answer here is incorrect as they fail to address the issue of reserved filenames – Paul Childs May 31 '22 at 22:17

7 Answers7

148

Check whether filename.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 and !File.Exists(Path.Combine(someFolder, filename))

unknown6656
  • 2,765
  • 2
  • 36
  • 52
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    Check Phil HUnt's answer though; it appears that InvalidPathChars is an obsolete API, and you should use GetInvalidFileNameChars() instead. – GendoIkari Jan 10 '11 at 19:12
  • 4
    The array returned from this method is not guaranteed to contain the complete set of characters that are invalid in file and directory names. Also, that property is deprecated. – Ben Voigt Jan 10 '11 at 19:12
  • 7
    counter-example: `"http://www.microsoft.com/"` passes your test but is not a valid filename, ditto for `"::::"` – Ben Voigt Jan 10 '11 at 19:25
  • 3
    More counter-examples: " " is not valid and new String('x', 1024) is not valid either. – Rasmus Faber Jan 11 '11 at 20:33
  • Another counter-example: ".txt" is not valid, but has no invalid characters. I would recommend splitting off the extension, and checking the filename and the extension (using the method above). Also make sure both are at least one character in length. – shellster Apr 01 '13 at 12:42
  • 1
    @shellster: `.txt` is a valid filename (as in `.gitignore`). I'm not sure how valid `.` is. – SLaks Apr 03 '13 at 03:27
  • 1
    @SLaks You are correct, however it is not "valid" as per Windows file rename through Explorer. Your response list a better example. Another one would be "..". Both are valid in filenames but are not valid by themselves. – shellster Apr 03 '13 at 18:50
  • 2
    @shellster You can rename a file to ".txt" in windows explorer by telling explorer to rename the file to ".txt.". Just a tip (or ".txt........" will work too). – Robert McKee Jul 06 '18 at 19:28
  • 5
    that should be `< 0` instead of `>= 0` as `IndexOfAny()` will return -1 if none are found – MikeT Apr 26 '20 at 15:33
  • @Michael I spotted that too, but wouldn't `== -1` be better though? – n00dles Jan 11 '21 at 23:45
  • @n00dles For code clarity I'd say you're right. Performance-wise, believe it'd be the same. Bug-wise, I was scarred once by some check I was doing that was specific, i.e. `==`, and since then, I've always done checks that exclude any invalid results. Imagine the new guy at Microsoft accidentally typo'd the `return -1;` to -2. My code would be robust enough to handle it. At least, that's my story and (for now), I'm sticking to it, lol. – MikeT Jan 12 '21 at 04:45
  • @Michael yeah makes sense really. Robustness! – n00dles Jan 13 '21 at 17:34
  • Related issue: File path exceeds MAX_PATH when long paths are not enabled. That'll blow up the `File.Exists()` call. I wonder if there's a file operation that can be called and that is known to fail with an exception if an invalid path is passed. – Rick Strahl Aug 14 '23 at 19:07
42

Check against GetInvalidFileNameChars():

var isValid = !string.IsNullOrEmpty(fileName) &&
              fileName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
              !File.Exists(Path.Combine(sourceFolder, fileName));
Phil Hunt
  • 8,404
  • 1
  • 30
  • 25
  • 3
    counter-example: `"http://www.microsoft.com/"` passes your test but is not a valid filename, ditto for `"::::"` – Ben Voigt Jan 10 '11 at 19:14
  • 1
    @Ben Voigt: You are correct that `::::` was incorrectly regarded as valid. I've corrected the answer to indicate `< 0` instead of `== 0`. – Phil Hunt Jan 10 '11 at 19:20
  • 2
    Oh, I just noticed you're using `GetInvalidFileNameChars`, not `GetInvalidPathChars` like SLaks. As long as it's a test for the filename only and not a filename-with-path, that'll be ok. – Ben Voigt Jan 10 '11 at 19:25
  • @Ben Voigt: Since the OP asked about validating file names, and paths only implicitly to validate file non-existence, I used `GetInvalidFileNameChars` :-) – Phil Hunt Jan 10 '11 at 19:29
13

If the file is going to be created, You should use a file dialog to specify the directory path. There's a short list of illegal characters for file names.

The only truly reliable way to tell if a file name is acceptable is to try it. Permissions is a morass.

ddyer
  • 1,792
  • 19
  • 26
  • 1
    The file dialog is a red herring, but +1 for "only truly reliable way ... is to try it". – Ben Voigt Jan 10 '11 at 19:17
  • 1
    Agreed. Even if the filename doesn't exist and contains no illegal characters, there are a number of reasons why the file won't get created, the most obvious being a lack of create permission to the directory. – Bob Kaufman Jan 10 '11 at 19:19
  • There are cases when you need to save to an unknown path, but you don't want the user to choose the exact path. In these cases, a file dialog is not viable. – Josh Noe Nov 25 '19 at 22:36
  • the main reason to suggest a file dialog is to figure out what **some** valid path is. If you have that, it can be modified as needed. – ddyer Sep 24 '22 at 22:22
2

I use this:

public static bool IsValidFileName(string name) {
    if(string.IsNullOrWhiteSpace(name)) return false;
    if(name.Length > 1 && name[1] == ':') {
        if(name.Length < 4 || name.ToLower()[0] < 'a' || name.ToLower()[0] > 'z' || name[2] != '\\') return false;
        name = name.Substring(3);
    }
    if(name.StartsWith("\\\\")) name = name.Substring(1);
    if(name.EndsWith("\\") || !name.Trim().Equals(name) || name.Contains("\\\\") ||
        name.IndexOfAny(Path.GetInvalidFileNameChars().Where(x=>x!='\\').ToArray()) >= 0) return false;
    return true;
}

Should take care of everything but reserved names, permissions, and length restrictions. This accepts both relative and absolute filenames.

Duke Nukem
  • 357
  • 2
  • 11
1

This is just an idea. One should populate the exception list:

public static bool IsValidFilename(string filename)
{
    try
    {
        File.OpenRead(filename).Close();
    }
    catch (ArgumentException) { return false; }
    catch (Exception) { }
    return true;
}
Bitterblue
  • 13,162
  • 17
  • 86
  • 124
1

For first part(Valid Filename), I use all ways and a temporary file creation to check if a file can be named as expected or throws an exception.
In some cases creating a file will not raise an exception until trying to delete it(eg: CON).
I also usa removePath arg to dictate it that file is just the name of file without its path.

using System.IO;
using System.Text;
private static readonly byte[] TestFileBytes = Encoding.ASCII.GetBytes(@"X");
public bool IsFileNameValid(string file, bool removePath = false)
{
    try
    {
        if (string.IsNullOrEmpty(file))
            return false;

        string fileNamePart = removePath ? Path.GetFileName(file) : file;
        if (fileNamePart.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
            return false;

        string fileName = Path.Combine(Path.GetTempPath(), fileNamePart);
        using FileStream fileStream = File.Create(fileName);
        {
            fileStream.Write(TestFileBytes, 0, TestFileBytes.Length);
        }

        File.Delete(fileName);
        return true;
    }
    catch
    {
        return false;
    }
}

If there is any denial of access to temp folder use a custom folder for creating test file.

This method will result false for . or .. or ... r any sequence of dot-only names in Windows, and also you can't create them manually, but those are not actually invalid names! those are uncreatable names for file or something like that ;).

And for next part(Not exists) just use: !File.Exists(yourFileNameWithPath).

shA.t
  • 16,580
  • 5
  • 54
  • 111
0

If you create a DirectoryInfo for the file, it will throw an exception if there are any problems with the file/directory name, either invalid chars or length.

DirectoryInfo di = new DirectoryInfo(myFileName);
Console.WriteLine("This filename/path could be valid. The folder might not exist yet though.")
if(di.Exists)
    Console.WriteLine("This file already exist.")

Not great that it is using exceptions for flow control, but you can be confident that it is right. OTOH, if you already planned to give the calling code an exception, mission accomplished.

Aki
  • 107
  • 8