Path
This is an important class
in .NET—Path
provides built-in methods. It helps when handling file paths. It is part of System.IO
.
In modern times .NET runs on Windows, macOS and Linux—each platform has different paths. By using the Path
class
, we can build programs that work on all these platforms.
GetFileName
exampleTaken all at once, the Path
class
might seem complicated and hard to use. But when we decompose it and look at individual methods, it is straightforward.
Path.GetFileName
, a static
method, on a path string
. It returns just the end part of the path.using System; using System.IO; string path = @"C:\programs\file.txt"; // Get file name. string filename = Path.GetFileName(path); Console.WriteLine("PATH: {0}", path); Console.WriteLine("FILENAME: {0}", filename);PATH: C:\programs\file.txt FILENAME: file.txt
Sometimes we want just the file name part, with no extension included. There is a special method for this purpose—we call it in this program.
string
"example" only.using System; using System.IO; string path = @"C:\programs\example.doc"; // Get file name without extension. string filename = Path.GetFileNameWithoutExtension(path); Console.WriteLine("PATH: {0}", path); Console.WriteLine("NO EXTENSION: {0}", filename);PATH: C:\programs\example.doc NO EXTENSION: example
GetExtension
exampleWhat if we want the extension part only, without the rest of the path? The GetExtension
method helps here. The leading period is included in the result.
GetExtension
handles extensions of 4 letters. It also handles the case where a file name has more than one period in it.GetExtension
. The period is part of the extension string
returned.using System; using System.IO; // ... Path values. string value1 = @"C:\perls\word.txt"; string value2 = @"C:\file.excel.dots.xlsx"; // ... Get extensions. string ext1 = Path.GetExtension(value1); string ext2 = Path.GetExtension(value2); Console.WriteLine(ext1); Console.WriteLine(ext2);.txt .xlsx
GetPathRoot
The "path root" is the volume name and its trailing separator. For the C drive, we get the value "C:\" in a string
. This can be used in Path.Combine
to build up full paths.
using System; using System.IO; string path = "C:\\images\\universe.jpg"; // Get path root. string root = Path.GetPathRoot(path); Console.WriteLine("PATH: {0}", path); Console.WriteLine("ROOT: {0}", root);PATH: C:\images\universe.jpg ROOT: C:\
GetDirectoryName
exampleWe often need to get the directory name from a string
path. The root, and the folder name are returned, without a trailing slash.
using System; using System.IO; string path = "C:\\images\\universe.jpg"; // Get directory name. string result = Path.GetDirectoryName(path); Console.WriteLine("PATH: {0}", path); Console.WriteLine("DIRECTORY: {0}", result);PATH: C:\images\universe.jpg DIRECTORY: C:\images
Path
syntaxIn path literals, we must use two backslashes "\\" unless we use verbatim string
syntax. A verbatim string
uses the "at" character as a prefix. In it, only one backslash is needed.
using System; // Verbatim string syntax. string value = @"C:\directory\word.txt"; string value2 = "C:\\directory\\word.txt"; Console.WriteLine("VALUE: {0}", value); Console.WriteLine("VALUE2: {0}", value2);VALUE: C:\directory\word.txt VALUE2: C:\directory\word.txt
Sometimes we have 2 or more parts, and want to merge them into a complete path. We can invoke Combine()
on the folder "Content\\" with the file name "file.txt."
Path.Combine
handles certain cases where we have directory separators in different positions.string
literal, we must use \\ (two backslashes).Path.Combine
are parts of a path. These can be the volume name, a directory name, or a file name.using System; using System.IO; // Combine 2 path parts. string path1 = Path.Combine("Content", "file.txt"); Console.WriteLine("COMBINE: {0}", path1); // Combine handles trailing separator. string path2 = Path.Combine("Content\\", "file.txt"); Console.WriteLine("COMBINE: {0}", path2); // Handle 3 parts. string path3 = Path.Combine("C:\\", "directory", "filename.txt"); Console.WriteLine("COMBINE: {0}", path3);COMBINE: Content\file.txt COMBINE: Content\file.txt COMBINE: C:\directory\filename.txt
GetRandomFileName
Sometimes programs use random file names. If we need to write a temp file and the path is not important, use Path.GetRandomFileName
.
GetRandomFileName
many times in a loop and get different results each time.using System; using System.IO; for (int i = 0; i < 5; i++) { // Get random file. string result = Path.GetRandomFileName(); Console.WriteLine("RANDOM: {0}", result); }RANDOM: idpqamj0.uba RANDOM: otyku52c.qlx RANDOM: e5mxa3p2.4cg RANDOM: auxhuuav.c2j RANDOM: epcjprlz.rwr
GetTempPath
This returns temporary file names. They point to a "Temp" folder in the User folder. This program shows the result of GetTempPath
.
GetTempPath()
has a separator character on the end. Path.GetDirectoryName
meanwhile does not.Path.Combine
, we can reliably concatenate a file name with the temporary path received.GetTempPath
is usually constant throughout program execution. It is safe to cache it—this eliminates allocations.using System; using System.IO; // Get temp path. string temp = Path.GetTempPath(); Console.WriteLine("TEMP PATH: {0}", temp); // Create a temp file path. string combined = Path.Combine(temp, "test.txt"); Console.WriteLine("TEMP FILE: {0}", combined);TEMP PATH: C:\Users\Sam\AppData\Local\Temp\ TEMP FILE: C:\Users\Sam\AppData\Local\Temp\test.txt
We can change the extension on a path string
with ChangeExtension
. This is a more graceful approach than using Replace()
on the string
itself.
using System; using System.IO; // Change extension of file name. string temp = "bird.doc"; string result = Path.ChangeExtension(temp, ".txt"); Console.WriteLine($"{temp} -> {result}");bird.doc -> bird.txt
There are 2 properties for separators. These help us develop code that is understandable. It may be easier to understand the name Path.DirectorySeparatorChar
.
"\\""/"
A program should expect that invalid characters will be encountered. We need to quickly detect invalid path characters.
Path.GetInvalidFileNameChars
and Path.GetInvalidPathChars
methods.Path.GetInvalidFileNameChars
and Path.GetInvalidPathChars
with a Dictionary
.using System; using System.Collections.Generic; using System.IO; using System.Linq; class Program { static void Main() { // First, we build a Dictionary of invalid characters. var dict = GetInvalidFileNameChars(); // Next, we test the dictionary to see if the asterisk (star) is valid. if (dict.ContainsKey('*')) { // This will run. // ... The star is in the Dictionary. Console.WriteLine("* is an invalid char"); } } /// <summary> /// Get a Dictionary of the invalid file name characters. /// </summary> static Dictionary<char, bool> GetInvalidFileNameChars() { // This method uses lambda expressions with ToDictionary. return Path.GetInvalidFileNameChars().ToDictionary(c => c, c => true); } }* is an invalid char
URLs and virtual
paths are used in ASP.NET websites. The Path
class
does not work well for these paths. For each ASP.NET request, there is a Request.PhysicalPath
.
Request.PhysicalPath
value is a Windows-style path. It works well with the Path
class
.// // This could be in your Global.asax file or in an ASPX page. // It gets the physical path. // string physical = Request.PhysicalPath; // // Here we see if we are handling an ASPX file. // if (Path.GetExtension(physical) == ".aspx") { // // Get the file name without an extension. // string key = Path.GetFileNameWithoutExtension(physical); }
We must be careful not to change the functionality too much. Path
methods contain steps for special cases. We can remove these branches.
Path
GetFileNameWithoutExtension
method.Path
method that is built into the .NET Framework. This supports more features.using System; using System.Diagnostics; using System.IO; class Program { public static string GetFileNameWithoutExtensionFast(string value) { // Find last available character. // ... This is either last index or last index before last period. int lastIndex = value.Length - 1; for (int i = lastIndex; i >= 1; i--) { if (value[i] == '.') { lastIndex = i - 1; break; } } // Find first available character. // ... Is either first character or first character after closest / // ... or \ character after last index. int firstIndex = 0; for (int i = lastIndex - 1; i >= 0; i--) { switch (value[i]) { case '/': case '\\': { firstIndex = i + 1; goto End; } } } End: // Return substring. return value.Substring(firstIndex, (lastIndex - firstIndex + 1)); } const int _max = 1000000; static void Main() { const string fileName = @"C:\test\ok.txt"; Console.WriteLine("TEST: {0}={1}", GetFileNameWithoutExtensionFast(fileName), Path.GetFileNameWithoutExtension(fileName)); // Version 1: use optimized method. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (GetFileNameWithoutExtensionFast(fileName) == null) { return; } } s1.Stop(); // Version 2: use Path method. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (Path.GetFileNameWithoutExtension(fileName) == null) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }TEST: ok=ok 21.60 ns Optimized method 43.21 ns Path method
Often we need lists of files in certain directories. We show how to get recursive lists of files by traversing subdirectories.
Sometimes additional logic is needed. For example, we can store a list of reserved file names. Then we can test to see if a file name is reserved.
The Path
class
provides Windows-native path manipulations and tests. It is ideal for file names, directory names, relative paths and file name extensions.