79

I have a path and I need to determine if it is a directory or file.

Is this the best way to determine if the path is a file?

string file = @"C:\Test\foo.txt";

bool isFile = !System.IO.Directory.Exists(file) && 
                         System.IO.File.Exists(file);

For a directory I would reverse the logic.

string directory = @"C:\Test";

bool isDirectory = System.IO.Directory.Exists(directory) && 
                            !System.IO.File.Exists(directory);

If both don't exists that then I won't go do either branch. So assume they both do exists.

David Basarab
  • 72,212
  • 42
  • 129
  • 156
  • http://stackoverflow.com/a/17198139/128506 – HAL9000 Jun 29 '14 at 22:30
  • Possible duplicate of [Better way to check if a Path is a File or a Directory?](http://stackoverflow.com/questions/1395205/better-way-to-check-if-a-path-is-a-file-or-a-directory) – DavidRR Sep 07 '16 at 12:50

8 Answers8

123

Use:

System.IO.File.GetAttributes(string path)

and check whether the returned FileAttributes result contains the value FileAttributes.Directory:

bool isDir = (File.GetAttributes(path) & FileAttributes.Directory)
                 == FileAttributes.Directory;
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 16
    Just make sure you do this in a try/catch, since the path may not even exist. – LBushkin Jun 25 '10 at 20:08
  • What happens if the path has wildcards in it? I get an illegal character argument, but what if I use wildcards and that's a legitimate path for my users? – Michael Sheely Oct 14 '16 at 15:22
  • 1
    @MichaelSheely - AFAIK, to deal with wildcards you have to call `Directory.GetFiles` with the string, and see whether the result has a count greater than zero. A "path with wildcards" isn't a *path*. It is a *pattern*. – ToolmakerSteve Apr 02 '18 at 12:08
  • will not work if path(dir of file) does not exist – IulianT Oct 13 '21 at 15:57
  • @LeresAldtai it is assumed that the path does exist - the OP didn't ask for a check that the path is non-existent. – Alnitak Oct 14 '21 at 09:41
57

I think this is the simplest way where you only need two checks:

string file = @"C:\tmp";
if (System.IO.Directory.Exists(file))
{
    // do stuff when file is an existing directory
}
else if (System.IO.File.Exists(file))
{
    // do stuff when file is an existing file
}
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • I did not downvote you, but it the accepted answer is surely "simpler" and only needs one call (not two). – Christian.K Jan 15 '09 at 12:00
  • Well, it depends, I personally would consider isDir = System.IO.Directory.Exists(file) to be "simpler" than the accepted solution. – Dirk Vollmar Jan 27 '09 at 15:47
  • 9
    The advantage of this approach is that it works even if there is no matching entry in the filesystem at all (in other words, it is neither a folder nor a file); In that sense, it is "simpler". Incidentally, the OP said "a path", and not "a path that is known to exist". – Tao Jul 15 '11 at 23:29
  • 6
    Yeah, I actually think this is the better answer when you're not sure the path you're investigating is there or not. Not having to worry about catching exceptions is a big deal. – Ben Collins Sep 26 '11 at 18:56
  • 1
    I agree that this method is effective. I just created a tiny utility to identify whether the specified name is a file or directory and this method made it extremely simple to return `1` for directory, `2` for file, and `0` for error (e.g., the name is invalid). The tool is tiny (338 bytes source, 3584 bytes compiled) and runs very fast. – Synetech Feb 06 '13 at 06:26
  • Do not depend on this answer, it is misleading. This will tell you if the File or Directory exist, NOT what type of IO entry they are. If the path is incorrect both checks will return false. – Tom Padilla Sep 11 '14 at 16:03
  • 2
    @TomPadilla: What exactly would you expect to happen if the path does not exist? – Dirk Vollmar Sep 26 '14 at 09:29
  • @xA3. The OP wanted to know if a path was a file or directory and didn't leave open the option of the path not existing at all. Since we can name anything in any way we want (we're not stuck with 8.3 as the olden days) I was trying to get the idea that the path is not enough. You need an actual IO object to determine if a thing is a file or dir. You cannot tell by the name AT ALL. As opposed to the accepted answer that will throw an exception if the path is invalid. – Tom Padilla Sep 26 '14 at 12:39
  • @TomPadilla No. BOTH approaches correctly identify all 3 states. This one by a pair of tests: if both are false, then the path is invalid (or you don't have permission). The other one by an exception in that case. Describe a situation where this approach differs from the approach you prefer. – ToolmakerSteve Sep 23 '15 at 12:25
  • You don't even need the File.Exists() check. If it exists but isn't a directory, then it must be a file. – Chris Bordeman Apr 07 '19 at 03:36
11

You can do this with some interop code:

    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAsAttribute(UnmanagedType.Bool)]
    public static extern bool PathIsDirectory([MarshalAsAttribute(UnmanagedType.LPWStr), In] string pszPath);

To further clarify some of the comments...

Introducing unmanaged code in this is not any more inherintly dangerous than any of the other file or I/O related calls in .NET since they ultimatley all call in to unmanaged code.

This is a single function call using a string. You aren't introducing any new data types and/or memory usage by calling this function. Yes, you do need to rely on the unmanaged code to properly clean up, but you ultimately have that dependency on most of the I/O related calls.

For reference, here is the code to File.GetAttributes(string path) from Reflector:

public static FileAttributes GetAttributes(string path)
{
    string fullPathInternal = Path.GetFullPathInternal(path);
    new FileIOPermission(FileIOPermissionAccess.Read, new string[] { fullPathInternal }, false, false).Demand();
    Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
    int errorCode = FillAttributeInfo(fullPathInternal, ref data, false, true);
    if (errorCode != 0)
    {
        __Error.WinIOError(errorCode, fullPathInternal);
    }
    return (FileAttributes) data.fileAttributes;
}

As you can see, it is also calling in to unmanaged code in order to retrieve the file attributes, so the arguements about introducing unmanaged code being dangerous are invalid. Likewise, the argument about staying completely in managed code. There is no managed code implementation to do this. Even calling File.GetAttributes() as the other answers propose have the same "issues" of calling unmanged code and I believe this is the more reliable method to accomplish determining if a path is a directory.

Edit To answer the comment by @Christian K about CAS. I believe the only reason GetAttributes makes the security demand is because it needs to read the properties of the file so it wants to make sure the calling code has permission to do so. This is not the same as the underlying OS checks (if there are any). You can always create a wrapper function around the P/Invoke call to PathIsDirectory that also demands certain CAS permissions, if necessary.

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
  • 3
    That seems like allot of work to figure out if a path is a file or directory. – David Basarab Jan 13 '09 at 15:54
  • How is that a lot of work? You declare a single imported function and can then call it as if it were a native method. – Scott Dorman Jan 13 '09 at 15:58
  • 2
    I don't see why this is downvoted, although it would be much simpler to stay in managed code using .NET functionality only. – Dirk Vollmar Jan 13 '09 at 16:10
  • 1
    Introducing Unmanaged code to get if a path is a file or directory is dangerous. I have to relay on the calling function to clean up the memory properly or my code could have leaks. – David Basarab Jan 13 '09 at 16:11
  • I didn't downvote just because it's unmanaged code, I down voted because it's not part of the standard .net library – Alnitak Jan 13 '09 at 16:39
  • @Alnitak: The question never specified that he wanted only managed code, besides there really is no way to do it using only managed code. – Scott Dorman Jan 13 '09 at 16:43
  • I'm not sure if that is an issue, but the GetAttributes() does CAS using FileIOPermission, the native call does not (appart from the operating system checks, which are not neccessarly the same, are they?). – Christian.K Jan 15 '09 at 12:04
  • 1
    I still fail to see why this is getting downvoted as it correctly answers the question with a method that provides no level of ambiguitity. – Scott Dorman Jan 15 '09 at 19:36
  • 4
    That is the "beauty" of SO, even a good answers might be downvoted by some zealots ;) – Wodzu Jun 07 '10 at 08:04
  • 1
    The only possible problem with this solution I see, is a change of the API in future versions of the OS. It is the .NET framework that should be downvoted for not having this. Otherwise this seems a neat solution. I'm very in to performance, and this beats all the rest big time! So you got a +1 – JDC Feb 15 '12 at 13:51
  • I would never use this, but +1 for effort :) – nawfal Jun 13 '13 at 04:24
  • This is the most simple and direct answers, and this can avoid the PathTooLong issue, too. – Jason Ching Jan 06 '18 at 11:33
  • @ScottDorman - its getting downvoted because you have provided no rationale that justifies stepping outside of the available managed calls (regardless of what Reflector shows). If you can give a situation where this has a benefit, then the answer's existence is justified. Otherwise, its just teaching a bad habit, IMHO. There is a *fundamental* difference between a programmer explicitly importing a reference to unmanaged code, and using an existing managed call. As a code reviewer, my first question would be "why did you do this"? I have to double-check the implications more thoroughly. – ToolmakerSteve Apr 02 '18 at 12:17
  • @ScottDorman in answer to Alnitak, you say "besides there really is no way to do it using only managed code". In that case, what is wrong with the other answers, which only make managed calls? If you are saying "the managed calls eventually have to call unmanaged code" then you miss the point. I would much rather have programmers make existing managed calls, leaving it up to Microsoft to get the boundary between managed and unmanaged right, than to have them *unnecessarily* cross that boundary themselves. – ToolmakerSteve Apr 02 '18 at 12:31
7

Assuming the directory exists...

bool isDir = (File.GetAttributes(path) & FileAttributes.Directory)
                  == FileAttributes.Directory;
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
4

Check this out:

/// <summary>
/// Returns true if the given file path is a folder.
/// </summary>
/// <param name="Path">File path</param>
/// <returns>True if a folder</returns>
public bool IsFolder(string path)
{
    return ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory);
}

from http://www.jonasjohn.de/snippets/csharp/is-folder.htm

Dror
  • 7,255
  • 3
  • 38
  • 44
2

Read the file attributes:

FileAttributes att = System.IO.File.GetAttributes(PATH_TO_FILE);

Check for the Directory flag.

Igor Zelaya
  • 4,167
  • 4
  • 35
  • 52
1

Given that a particular path string cannot represent both a directory and a file, then the following works just fine and opens the door for other operations.

bool isFile = new FileInfo(path).Exists;
bool isDir = new DirectoryInfo(path).Exists;

If you're working with the file system, using FileInfo and DirectoryInfo is much simpler than using strings.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • See 0xA3's answer, slightly better calls imo. – nawfal Jun 13 '13 at 04:25
  • Or just if (Directory.Exists(src)) ... If it's a file, it'll be false. If src is a directory, it'll be false. If src is a directory that doesn't exist, then by definition src can't a directory. – Chris Bordeman Apr 07 '19 at 03:29
-4

Hmm, it looks like the Files class (in java.nio) actually has a static isDirectory method. So, I think you could actually use the following:

Path what = ...
boolean isDir = Files.isDirectory(what);
Jon7
  • 7,165
  • 2
  • 33
  • 39
Onikoroshi
  • 281
  • 4
  • 16