103

I am trying to determine whether a string input by a user is valid for representing a path to a folder. By valid, I mean formatted properly.

In my application, the folder represents an installation destination. Provided that the folder path is valid, I want to determine if the folder exists, and create it if it does not.

I am currently using IO.Directory.Exists( String path ). I find that this works fine except when the user does not format the string properly. When that happens, this method will return false which indicates that the folder does not exist. But this is a problem because I won't be able to create the folder afterwards.

From my googling I found a suggestion to use a regular expression to check if the format is proper. I have no experience with regular expressions, and am wondering if that is a viable approach. Here's what I found:

Regex r = new Regex( @"^(([a-zA-Z]\:)|(\\))(\\{1}|((\\{1})[^\\]([^/:*?<>""|]*))+)$" );
return r.IsMatch( path );

Would a regular expression test in combination with Directory.Exists(), give me a good enough method to check if the path is valid and whether it exists? I know this will vary with the OS and other factors, but the program is targeted for Windows users only.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
Pudpuduk
  • 1,549
  • 2
  • 14
  • 23
  • 1
    If it won't create the directory after Directory.Exists returns false, isn't that a pretty good indication that the user provided bad input? – Robert Harvey Jun 29 '10 at 00:34
  • 1
    http://stackoverflow.com/questions/3067479/figuring-out-whether-string-is-valid-file-path – Robert Harvey Jun 29 '10 at 00:35
  • http://stackoverflow.com/questions/422090/in-c-check-that-filename-is-possibly-valid-not-that-it-exists – Robert Harvey Jun 29 '10 at 00:36
  • 2
    @RobertI saw that question and it did not provide a specific answer other than general rules. The 2nd highest answer did not cover formatting but only invalid characters. Also the Directory.Exists method can return false, but since I want the option of creating the folder on the spot i cannot just go by that. – Pudpuduk Jun 29 '10 at 00:41
  • @Robert On the 2nd topic you linked - typing in a single word would still pass the validation given in the answers to that question. – Pudpuduk Jun 29 '10 at 00:46
  • possible duplicate of [How check if given string is legal (allowed) file name under Windows?](http://stackoverflow.com/questions/62771/how-check-if-given-string-is-legal-allowed-file-name-under-windows) – bummi Sep 27 '14 at 09:38

8 Answers8

139

Call Path.GetFullPath; it will throw exceptions if the path is invalid.

To disallow relative paths (such as Word), call Path.IsPathRooted.

fen
  • 9,835
  • 5
  • 34
  • 57
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • I knew there was something simpler! And thanks, I did not think of the paths-being-relative problem. – Pudpuduk Jun 29 '10 at 01:08
  • 4
    Thanks SLaks. I've seen many duplicates, and done many Google searches (on more than one occasion), but this is the first time I've seen a good answer to this particular question. – Robert Harvey Jun 29 '10 at 01:18
  • Path.GetFullPath("con.txt") working... But this is not valid file name – Ievgen Apr 30 '11 at 22:59
  • 5
    Path.GetFullPath("con.txt") is a valid file name. – Christoffer Aug 24 '12 at 21:32
  • 11
    @Slaks This is too old to leave a comment, but I still want to leave a one here for the reason I gave you my vote of -1. Path.GetFullPath() seems to work OK, but what if the path is: "Z:\\\\\\\\Hi\\\\\\There", it is not a valid absolute path but the Path.GetFullPath(...) gives out the result: Z:\Hi\There and there is no exception raised. I had to change it a little by comparing the string returned by GetFullPath() and the original string like this: private bool IsPathValid(string path){ try { string fullPath=Path.GetFullPath(path); return fullPath==path; } catch { return false;} } – King King May 10 '13 at 21:34
  • Wasted a whole day because I was dynamically building a path with "con" as a folder name. This seemed to solve it ... thanks. – Brian Mar 03 '14 at 17:09
  • @KingKing Your example path with the extra '\' characters *is* valid. It is just not a [canonical path](http://stackoverflow.com/q/12100299/1497596). – DavidRR Dec 08 '16 at 20:28
  • @SLaks [Path.IsPathRooted](https://msdn.microsoft.com/en-us/library/system.io.path.ispathrooted) will return true for what some would consider *is* a relative path. For example, in the path `C:MyDir`, the directory `MyDir` is relative to `C:`. (Note the difference with the path `C:\MyDir`.) – DavidRR Dec 08 '16 at 20:33
  • Please consider [my answer](http://stackoverflow.com/a/41049011/1497596) if you wish to leverage the use of [Path.GetFullPath](https://msdn.microsoft.com/en-us/library/system.io.path.getfullpath(v=vs.110).aspx) without the burden of exception handling. – DavidRR Dec 08 '16 at 21:25
  • @DavidRR The OP asked for some valid ***Windows folder path***, so I don't think my example of path with extra \ is valid (pointing to something in Windows OS). Also note about the arbitrary number of \ we can type between any valid folder names, there may be some special path which is valid, but the arbitrary number of paths would result in many invalid paths in Windows OS. – King King Dec 09 '16 at 03:11
  • 4
    @KingKing From [this Linux answer](http://unix.stackexchange.com/a/1919) on unix.stackexchange.com: *"Multiple slashes are allowed and are equivalent to a single slash.."* I have observed the same on Windows (though the leading slashes in a UNC path may be treated differently). For evidence of this, at a Command Prompt, try this: `cd C:\\\\\\\Windows\\\\\\\System32`. For Windows, I cannot find an authoritative source that documents this behavior, but of course would welcome a pointer to one. – DavidRR Dec 09 '16 at 19:43
  • What if you want to check if a path is a valid syntax, not if the path actually is present on the system? – Gregory William Bryant Apr 27 '20 at 15:32
  • 2
    This will not catch all invalid paths; for example, it doesn't catch that `"c:\e:\g:\"` is invalid. – Kevin Sep 06 '21 at 23:23
  • It may be an old question, but I would like to add suggesting using `Path.IsPathFullyQualified` – M. Elghamry Oct 31 '21 at 00:13
17

I actually disagree with SLaks. That solution did not work for me. Exception did not happen as expected. But this code worked for me:

if(System.IO.Directory.Exists(path))
{
    ...
}
Grant Winney
  • 65,241
  • 13
  • 115
  • 165
  • 79
    A valid path is not necessarily a directory that exists... which is exactly the problem asked here – Benlitz Sep 06 '13 at 08:21
  • 2
    question was related to validation of the path string, a path that might not exist. – Mubashar Dec 02 '13 at 01:11
  • I think this way is correct. Exceptions should not be expected. This method also check incorrect characters in the given path. – Eugene Maksimov Jul 07 '16 at 13:44
  • This condition will itself throw an exception if the "path" is found not a real path since Directory.Exists requires a valid path. – M. Fawad Surosh Mar 20 '17 at 20:29
  • Completely wrong answer! I wonder how it received 32 upvotes (as of now). Must be by people who were looking at the wrong place for the problem they were facing, and they stumbled upon this. – Sнаđошƒаӽ Feb 13 '19 at 06:59
  • @Sнаđошƒаӽ it's not that the answer is wrong, it's that the title is wrong. The title only asks if the name is valid, not if the directory exists. However, later the OP himself suggests using Directory.Exists() and says he wants a "method to check if the path is valid and whether it exists". – Jamie Jul 08 '21 at 20:10
16

Path.GetFullPath gives below exceptions only

ArgumentException path is a zero-length string, contains only white space, or contains one or more of the invalid characters defined in GetInvalidPathChars. -or- The system could not retrieve the absolute path.

SecurityException The caller does not have the required permissions.

ArgumentNullException path is null.

NotSupportedException path contains a colon (":") that is not part of a volume identifier (for example, "c:\").

PathTooLongException The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.

Alternate way is to use the following :

/// <summary>
/// Validate the Path. If path is relative append the path to the project directory by default.
/// </summary>
/// <param name="path">Path to validate</param>
/// <param name="RelativePath">Relative path</param>
/// <param name="Extension">If want to check for File Path</param>
/// <returns></returns>
private static bool ValidateDllPath(ref string path, string RelativePath = "", string Extension = "")
{
    // Check if it contains any Invalid Characters.
    if (path.IndexOfAny(Path.GetInvalidPathChars()) == -1)
    {
        try
        {
            // If path is relative take %IGXLROOT% as the base directory
            if (!Path.IsPathRooted(path))
            {
                if (string.IsNullOrEmpty(RelativePath))
                {
                    // Exceptions handled by Path.GetFullPath
                    // ArgumentException path is a zero-length string, contains only white space, or contains one or more of the invalid characters defined in GetInvalidPathChars. -or- The system could not retrieve the absolute path.
                    // 
                    // SecurityException The caller does not have the required permissions.
                    // 
                    // ArgumentNullException path is null.
                    // 
                    // NotSupportedException path contains a colon (":") that is not part of a volume identifier (for example, "c:\"). 
                    // PathTooLongException The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.

                    // RelativePath is not passed so we would take the project path 
                    path = Path.GetFullPath(RelativePath);

                }
                else
                {
                    // Make sure the path is relative to the RelativePath and not our project directory
                    path = Path.Combine(RelativePath, path);
                }
            }

            // Exceptions from FileInfo Constructor:
            //   System.ArgumentNullException:
            //     fileName is null.
            //
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
            //
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
            //
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
            //
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
            FileInfo fileInfo = new FileInfo(path);

            // Exceptions using FileInfo.Length:
            //   System.IO.IOException:
            //     System.IO.FileSystemInfo.Refresh() cannot update the state of the file or
            //     directory.
            //
            //   System.IO.FileNotFoundException:
            //     The file does not exist.-or- The Length property is called for a directory.
            bool throwEx = fileInfo.Length == -1;

            // Exceptions using FileInfo.IsReadOnly:
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
            //     The file described by the current System.IO.FileInfo object is read-only.-or-
            //     This operation is not supported on the current platform.-or- The caller does
            //     not have the required permission.
            throwEx = fileInfo.IsReadOnly;

            if (!string.IsNullOrEmpty(Extension))
            {
                // Validate the Extension of the file.
                if (Path.GetExtension(path).Equals(Extension, StringComparison.InvariantCultureIgnoreCase))
                {
                    // Trim the Library Path
                    path = path.Trim();
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;

            }
        }
        catch (ArgumentNullException)
        {
            //   System.ArgumentNullException:
            //     fileName is null.
        }
        catch (System.Security.SecurityException)
        {
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
        }
        catch (ArgumentException)
        {
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
        }
        catch (UnauthorizedAccessException)
        {
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
        }
        catch (PathTooLongException)
        {
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
        }
        catch (NotSupportedException)
        {
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
        }
        catch (FileNotFoundException)
        {
            // System.FileNotFoundException
            //  The exception that is thrown when an attempt to access a file that does not
            //  exist on disk fails.
        }
        catch (IOException)
        {
            //   System.IO.IOException:
            //     An I/O error occurred while opening the file.
        }
        catch (Exception)
        {
            // Unknown Exception. Might be due to wrong case or nulll checks.
        }
    }
    else
    {
        // Path contains invalid characters
    }
    return false;
}
vCillusion
  • 1,749
  • 20
  • 33
11

Here is a solution that leverages the use of Path.GetFullPath as recommended in the answer by @SLaks.

In the code that I am including here, note that IsValidPath(string path) is designed such that the caller does not have to worry about exception handling.

You may also find that the method that it calls, TryGetFullPath(...), also has merit on its own when you wish to safely attempt to get an absolute path.

/// <summary>
/// Gets a value that indicates whether <paramref name="path"/>
/// is a valid path.
/// </summary>
/// <returns>Returns <c>true</c> if <paramref name="path"/> is a
/// valid path; <c>false</c> otherwise. Also returns <c>false</c> if
/// the caller does not have the required permissions to access
/// <paramref name="path"/>.
/// </returns>
/// <seealso cref="Path.GetFullPath"/>
/// <seealso cref="TryGetFullPath"/>
public static bool IsValidPath(string path)
{
    string result;
    return TryGetFullPath(path, out result);
}

/// <summary>
/// Returns the absolute path for the specified path string. A return
/// value indicates whether the conversion succeeded.
/// </summary>
/// <param name="path">The file or directory for which to obtain absolute
/// path information.
/// </param>
/// <param name="result">When this method returns, contains the absolute
/// path representation of <paramref name="path"/>, if the conversion
/// succeeded, or <see cref="String.Empty"/> if the conversion failed.
/// The conversion fails if <paramref name="path"/> is null or
/// <see cref="String.Empty"/>, or is not of the correct format. This
/// parameter is passed uninitialized; any value originally supplied
/// in <paramref name="result"/> will be overwritten.
/// </param>
/// <returns><c>true</c> if <paramref name="path"/> was converted
/// to an absolute path successfully; otherwise, false.
/// </returns>
/// <seealso cref="Path.GetFullPath"/>
/// <seealso cref="IsValidPath"/>
public static bool TryGetFullPath(string path, out string result)
{
    result = String.Empty;
    if (String.IsNullOrWhiteSpace(path)) { return false; }
    bool status = false;

    try
    {
        result = Path.GetFullPath(path);
        status = true;
    }
    catch (ArgumentException) { }
    catch (SecurityException) { }
    catch (NotSupportedException) { }
    catch (PathTooLongException) { }

    return status;
}
Community
  • 1
  • 1
DavidRR
  • 18,291
  • 25
  • 109
  • 191
7
    private bool IsValidPath(string path)
    {
        Regex driveCheck = new Regex(@"^[a-zA-Z]:\\$");
        if (!driveCheck.IsMatch(path.Substring(0, 3))) return false;
        string strTheseAreInvalidFileNameChars = new string(Path.GetInvalidPathChars());
        strTheseAreInvalidFileNameChars += @":/?*" + "\"";
        Regex containsABadCharacter = new Regex("[" + Regex.Escape(strTheseAreInvalidFileNameChars) + "]");
        if (containsABadCharacter.IsMatch(path.Substring(3, path.Length - 3)))
            return false;

        DirectoryInfo dir = new DirectoryInfo(Path.GetFullPath(path));
        if (!dir.Exists)
            dir.Create();
        return true;
    }
Ghasem
  • 14,455
  • 21
  • 138
  • 171
6

Use this Code

string DirectoryName = "Sample Name For Directory Or File";
Path.GetInvalidFileNameChars()
  .Where(x => DirectoryName.Contains(x))
  .Count() > 0 || DirectoryName == "con"
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
user1508188
  • 97
  • 1
  • 3
  • 4
    Slightly shorter code accomplishing the same thing: `Path.GetInvalidFileNameChars().Any(DirectoryName.Contains) || DirectoryName == "con"` – bsegraves Oct 15 '12 at 18:06
  • 2
    @nawfal Indeed. From [Naming Files, Paths, and Namespaces](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx) on MSDN: *"Do not use the following reserved names for the name of a file: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. Also avoid these names followed immediately by an extension; for example, NUL.txt is not recommended. For more information, see [Namespaces](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces)."* – DavidRR Dec 09 '16 at 19:58
  • This "blacklist aproach" does not work on every windows system, for example when a pictographs shows up: https://en.wikipedia.org/wiki/Miscellaneous_Symbols_and_Pictographs – Jan Apr 24 '18 at 10:28
6

A simpler OS-independent solution:

Go ahead and attempt to create the actual directory; if there is an issue or the name is invalid, the OS will automatically complain and the code will throw.

public static class PathHelper
{
    public static void ValidatePath(string path)
    {
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path).Delete();
    }
}

Usage:

try
{
    PathHelper.ValidatePath(path);
}
catch(Exception e)
{
    // handle exception
}

Directory.CreateDirectory() will automatically throw in all of the following situations:

System.IO.IOException:
The directory specified by path is a file. -or- The network name is not known.

System.UnauthorizedAccessException:
The caller does not have the required permission.

System.ArgumentException:
path is a zero-length string, contains only white space, or contains one or more invalid characters. You can query for invalid characters by using the System.IO.Path.GetInvalidPathChars method. -or- path is prefixed with, or contains, only a colon character (:).

System.ArgumentNullException:
path is null.

System.IO.PathTooLongException:
The specified path, file name, or both exceed the system-defined maximum length.

System.IO.DirectoryNotFoundException:
The specified path is invalid (for example, it is on an unmapped drive).

System.NotSupportedException:
path contains a colon character (:) that is not part of a drive label ("C:").

SNag
  • 17,681
  • 10
  • 54
  • 69
3

I haven't had any problems with this code:

private bool IsValidPath(string path, bool exactPath = true)
{
    bool isValid = true;

    try
    {
        string fullPath = Path.GetFullPath(path);

        if (exactPath)
        {
            string root = Path.GetPathRoot(path);
            isValid = string.IsNullOrEmpty(root.Trim(new char[] { '\\', '/' })) == false;
        }
        else
        {
            isValid = Path.IsPathRooted(path);
        }
    }
    catch(Exception ex)
    {
        isValid = false;
    }

    return isValid;
}

For example these would return false:

IsValidPath("C:/abc*d");
IsValidPath("C:/abc?d");
IsValidPath("C:/abc\"d");
IsValidPath("C:/abc<d");
IsValidPath("C:/abc>d");
IsValidPath("C:/abc|d");
IsValidPath("C:/abc:d");
IsValidPath("");
IsValidPath("./abc");
IsValidPath("/abc");
IsValidPath("abc");
IsValidPath("abc", false);

And these would return true:

IsValidPath(@"C:\\abc");
IsValidPath(@"F:\FILES\");
IsValidPath(@"C:\\abc.docx\\defg.docx");
IsValidPath(@"C:/abc/defg");
IsValidPath(@"C:\\\//\/\\/\\\/abc/\/\/\/\///\\\//\defg");
IsValidPath(@"C:/abc/def~`!@#$%^&()_-+={[}];',.g");
IsValidPath(@"C:\\\\\abc////////defg");
IsValidPath(@"/abc", false);
Dao Seeker
  • 214
  • 2
  • 11