Path.Combine() / Path.GetFileName()
| Since: | C# 1.0(2002) |
|---|
Path.Combine() safely joins file paths, and Path.GetFileName() extracts the filename portion from a path.
Syntax
// Joins paths using the OS path separator automatically Path.Combine(string path1, string path2) Path.Combine(params string[] paths) // Gets the filename including the extension from a path Path.GetFileName(string path) // Gets the filename without the extension Path.GetFileNameWithoutExtension(string path) // Gets the extension (with a leading dot) Path.GetExtension(string path) // Gets the directory portion of the path Path.GetDirectoryName(string path)
Method List
| Method | Description |
|---|---|
| Path.Combine(path1, path2) | Joins two or more paths. Automatically inserts the OS separator (\ on Windows, / on Linux/Mac). |
| Path.GetFileName(path) | Returns only the filename (with extension) from the path string. |
| Path.GetFileNameWithoutExtension(path) | Returns the filename without the extension. |
| Path.GetExtension(path) | Returns the extension (e.g., .txt). Returns an empty string if there is no extension. |
| Path.GetDirectoryName(path) | Returns the directory portion of the path, excluding the filename. |
| Path.GetFullPath(path) | Converts a relative path to an absolute path. |
| Path.GetTempPath() | Returns the path of the system's temporary file directory. |
Sample Code
Program.cs
using System; using System.IO; // Joins paths using Path.Combine() string folder = @"C:\Users\eva_user\documents"; string fileName = "report.txt"; string fullPath = Path.Combine(folder, fileName); Console.WriteLine(fullPath); // C:\Users\eva_user\documents\report.txt // Can join three or more paths at once string savePath = Path.Combine(@"C:\data", "2024", "01", "log.csv"); Console.WriteLine(savePath); // C:\data\2024\01\log.csv // Extracts filename components with Path.GetFileName() and related methods string path = @"C:\Users\eva_user\documents\report.txt"; Console.WriteLine(Path.GetFileName(path)); // report.txt Console.WriteLine(Path.GetFileNameWithoutExtension(path)); // report Console.WriteLine(Path.GetExtension(path)); // .txt Console.WriteLine(Path.GetDirectoryName(path)); // C:\Users\eva_user\documents // Gets the system temporary directory string tempDir = Path.GetTempPath(); Console.WriteLine(tempDir); // e.g., C:\Users\eva_user\AppData\Local\Temp\
This produces the following output:
dotnet script path_combine_getfilename.csx C:\Users\eva_user\documents\report.txt C:\data\2024\01\log.csv report.txt report .txt C:\Users\eva_user\documents C:\Users\eva_user\AppData\Local\Temp\
The output above is from a Windows environment. On macOS/Linux, the separator is a forward slash (/).
Practical Pattern: Generating Dated Log File Paths
A pattern for generating a log file path based on the current date. Produces a consistent path no matter how many times it is called.
LogPathBuilder.cs
using System;
using System.IO;
string logDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"EvaApp",
"logs"
);
// Generates a dated log filename
string logFileName = $"app_{DateTime.Today:yyyyMMdd}.log";
string logFilePath = Path.Combine(logDir, logFileName);
Console.WriteLine($"Log directory: {logDir}");
Console.WriteLine($"Log file: {logFilePath}");
// Creates the directory and appends to the log file
Directory.CreateDirectory(logDir);
File.AppendAllText(logFilePath, $"[{DateTime.Now:HH:mm:ss}] Started\n");
// Breaks down the path for verification
Console.WriteLine($"Filename: {Path.GetFileName(logFilePath)}");
Console.WriteLine($"Extension: {Path.GetExtension(logFilePath)}");
Console.WriteLine($"Directory: {Path.GetDirectoryName(logFilePath)}");
// Cleanup
Directory.Delete(logDir, recursive: true);
This produces the following output:
dotnet script log_path_builder.csx Log directory: /Users/eva_user/Library/Application Support/EvaApp/logs Log file: /Users/eva_user/Library/Application Support/EvaApp/logs/app_20240115.log Filename: app_20240115.log Extension: .log Directory: /Users/eva_user/Library/Application Support/EvaApp/logs
Practical Pattern: Filtering Files by Extension
A pattern for processing only files with specific extensions from a directory listing.
ExtensionFilter.cs
using System;
using System.IO;
using System.Linq;
// Sets up test files
string testDir = "test_assets";
Directory.CreateDirectory(testDir);
File.WriteAllText(Path.Combine(testDir, "shinji.png"), "png");
File.WriteAllText(Path.Combine(testDir, "rei.jpg"), "jpg");
File.WriteAllText(Path.Combine(testDir, "asuka.png"), "png");
File.WriteAllText(Path.Combine(testDir, "kaworu.txt"), "txt");
File.WriteAllText(Path.Combine(testDir, "misato.json"), "json");
// Lists all files and their extensions
string[] allFiles = Directory.GetFiles(testDir);
Console.WriteLine("--- All files ---");
foreach (string f in allFiles)
{
string name = Path.GetFileName(f);
string ext = Path.GetExtension(f);
Console.WriteLine($" {name} (ext: {ext})");
}
// Extracts image files (.png, .jpg) only
var imageFiles = allFiles
.Where(f => Path.GetExtension(f).ToLower() is ".png" or ".jpg")
.Select(f => Path.GetFileName(f))
.ToList();
Console.WriteLine($"--- Image files ({imageFiles.Count}) ---");
foreach (string img in imageFiles)
Console.WriteLine($" {img}");
// Cleanup
Directory.Delete(testDir, recursive: true);
This produces the following output:
dotnet script extension_filter.csx --- All files --- asuka.png (ext: .png) kaworu.txt (ext: .txt) misato.json (ext: .json) rei.jpg (ext: .jpg) shinji.png (ext: .png) --- Image files (3) --- asuka.png rei.jpg shinji.png
Common Mistakes
Common Mistake 1: Building Paths by String Concatenation
Manually concatenating strings to build paths leads to OS-specific separator issues and duplicate or missing slashes.
path_bad.cs
using System; using System.IO; // NG: Hardcoding separators causes OS-specific issues and duplicate slashes string bad = "work" + "/" + "logs" + "/" + "app.log"; // Windows expects \ but / is used // NG: Trailing slash causes double slash string dir = "work/logs/"; string badPath = dir + "app.log"; // work/logs//app.log
path_good.cs
using System;
using System.IO;
// OK: Path.Combine() inserts the correct separator automatically
string good = Path.Combine("work", "logs", "app.log");
Console.WriteLine(good); // Windows: work\logs\app.log macOS: work/logs/app.log
// OK: Handles trailing slashes correctly
string goodPath = Path.Combine("work/logs/", "app.log");
Console.WriteLine(goodPath); // work/logs/app.log (no duplication)
This produces the following output:
dotnet run work/logs/app.log work/logs/app.log
Common Mistake 2: GetDirectoryName Returning null
Passing a root path or a bare filename (with no directory part) to GetDirectoryName returns null or an empty string.
using System;
using System.IO;
// NG: Not accounting for null can cause a NullReferenceException
string rootPath = @"C:\";
string? dir1 = Path.GetDirectoryName(rootPath); // Returns null
Console.WriteLine(dir1 ?? "null"); // null
string bareFile = "report.txt"; // No directory part
string? dir2 = Path.GetDirectoryName(bareFile); // Returns "" (empty string)
Console.WriteLine($"Empty: '{dir2}'");
This produces the following output:
dotnet run null Empty: ''
using System;
using System.IO;
// OK: Add a null check
string? safeDir = Path.GetDirectoryName(@"C:\Users\eva_user\report.txt");
if (safeDir != null)
Console.WriteLine($"Directory: {safeDir}");
This produces the following output:
dotnet run Directory: C:\Users\eva_user
Overview
Path.Combine() is the safe way to join paths compared to string concatenation. It automatically corrects duplicate or missing separators, so the same code works on Windows, Linux, and Mac.
Manually concatenating paths with path1 + "\\" + path2 is prone to OS-specific separator differences, so always use Path.Combine().
For reading and writing files, see File.ReadAllText() / File.WriteAllText().
If you find any errors or copyright issues, please contact us.