yotiky Tech Blog

とあるエンジニアの備忘録

C# - Path を操作する

目次

検証環境

  • .NET Core 5.0

Path を操作する

エスケープと逐語的文字列リテラル

Console.WriteLine("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\Common7\\IDE");
Console.WriteLine(@"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE");

// 同じ文字列
// C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE
// C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE

セパレーター

Console.WriteLine($"Path.DirectorySeparatorChar: '{Path.DirectorySeparatorChar}'");
Console.WriteLine($"Path.AltDirectorySeparatorChar: '{Path.AltDirectorySeparatorChar}'");
Console.WriteLine($"Path.PathSeparator: '{Path.PathSeparator}'");
Console.WriteLine($"Path.VolumeSeparatorChar: '{Path.VolumeSeparatorChar}'");

// Windows:
//    Path.DirectorySeparatorChar: '\'
//    Path.AltDirectorySeparatorChar: '/'
//    Path.PathSeparator: ';'
//    Path.VolumeSeparatorChar: ':'
//    Path.GetInvalidPathChars:

// Linux:
//    Path.DirectorySeparatorChar: '/'
//    Path.AltDirectorySeparatorChar: '/'
//    Path.PathSeparator: ':'
//    Path.VolumeSeparatorChar: '/'

ディレクトリとファイル

var vs = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe";
Console.WriteLine($"Path.GetDirectoryname: {Path.GetDirectoryName(vs)}");
Console.WriteLine($"Path.GetExtension: {Path.GetExtension(vs)}");
Console.WriteLine($"Path.GetFileName: {Path.GetFileName(vs)}");
Console.WriteLine($"Path.GetFileNameWithoutExtension: {Path.GetFileNameWithoutExtension(vs)}");
Console.WriteLine($"Path.GetPathRoot: {Path.GetPathRoot(vs)}");

// Path.GetDirectoryname: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE
// Path.GetExtension: .exe
// Path.GetFileName: devenv.exe
// Path.GetFileNameWithoutExtension: devenv
// Path.GetPathRoot: C:\

GetRelativePath .NET Standard 2.1、 .NET Core 2.0 以降で使用可能。

Console.WriteLine($"Path.GetFullPath: {Path.GetFullPath(vs)}");
Console.WriteLine($"Path.GetFullPath: {Path.GetFullPath(@".\..\Tools\spyxx.exe", Path.GetDirectoryName(vs))}");
Console.WriteLine($"Path.GetRelativePath: {Path.GetRelativePath(@"C:\Program Files (x86)\Microsoft Visual Studio\", vs)}");
Console.WriteLine($"Path.ChangeExtension: {Path.ChangeExtension(vs, ".bin")}");

// Path.GetFullPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
// Path.GetFullPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools\spyxx.exe
// Path.GetRelativePath: 2019\Professional\Common7\IDE\devenv.exe
// Path.ChangeExtension: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.bin
var download = @"C:\Downloads\";
Console.WriteLine($"Path.EndsInDirectorySeparator: {Path.EndsInDirectorySeparator(download )}");
Console.WriteLine($"Path.TrimEndingDirectorySeparator: {Path.TrimEndingDirectorySeparator(download )}");

// Path.EndsInDirectorySeparator: True
// Path.TrimEndingDirectorySeparator: C:\Downloads

連結と結合

var unity = new[] {"C:", @"Program Files\Unity", @"Hub\Editor\", @"2019.4.3f1/Editor/Unity.exe"};
Console.WriteLine($"Path.Join: {Path.Join(unity)}");
Console.WriteLine($"Path.Combine: {Path.Combine(unity)}");
Console.WriteLine($"Path.GetFullPath(Mix separator): {Path.GetFullPath(Path.Combine(unity))}");
Console.WriteLine($"Path.Join(2root): {Path.Join("C:", @"1\2\3", "D:", @"4\5\6")}");
Console.WriteLine($"Path.Combine(2root): {Path.Combine("C:", @"1\2\3", "D:", @"4\5\6")}");

// Path.Join: C:\Program Files\Unity\Hub\Editor\2019.4.3f1/Editor/Unity.exe
// Path.Combine: C:\Program Files\Unity\Hub\Editor\2019.4.3f1/Editor/Unity.exe
// Path.GetFullPath(Mix separator): C:\Program Files\Unity\Hub\Editor\2019.4.3f1\Editor\Unity.exe
// Path.Join(2root): C:\1\2\3\D:\4\5\6
// Path.Combine(2root): D:\4\5\6

使用できない文字

char convert(char x) => char.IsControl(x) ? (char)(UnicodeRanges.ControlPictures.FirstCodePoint + (int)x) : x;
Console.WriteLine($"Path.GetInvalidPathChars: {string.Join(" ", Path.GetInvalidPathChars().Select(convert))}");
Console.WriteLine($"Path.GetInvalidFileNameChars: {string.Join(" ", Path.GetInvalidFileNameChars().Select(convert))}");

// Path.GetInvalidPathChars: | ␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟
// Path.GetInvalidFileNameChars: " < > | ␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟ : * ? \ /

ファイル名の自動生成

Console.WriteLine($"Path.GetRandomFileName: {Path.GetRandomFileName()}");
// snxcaq4r.ddm, 3gwra5ri.ixv, w4hcste4.gol, ...

絶対パス相対パス

Path クラス

GetRelativePath .NET Standard 2.1、 .NET Core 2.0 以降で使用可能。

// 絶対パス
var vs = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe";
// 相対パスを使って対象の絶対パスを取得
var currentEnv = Path.GetFullPath(@".\..\..\..\..", Path.GetDirectoryName(vs));
// 基準のパスから対象の相対パスを取得
var relative = Path.GetRelativePath(currentEnv, vs);

Console.WriteLine($"FullPath: {vs}");
Console.WriteLine($"CurrentEnv: {currentEnv}");
Console.WriteLine($"Path.GetRelativePath: {relative}");

// FullPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
// CurrentEnv: C:\Program Files (x86)\Microsoft Visual Studio
// Path.GetRelativePath: 2019\Professional\Common7\IDE\devenv.exe

Uri クラス

Uri クラス

Uri クラスを使用する場合は、基本的にセパレーターが「\」ではなく「/」なのとURLエンコード/デコードされる点に注意する。URLエンコードの対象文字列がパスに含まれている場合は誤変換してしまうの置換処理などが必要。

var uri = new Uri(vs);
Console.WriteLine($"AbsolutePath: {uri.AbsolutePath}");
Console.WriteLine($"AbsoluteUri: {uri.AbsoluteUri}");
Console.WriteLine($"DnsSafeHost: {uri.DnsSafeHost}");
Console.WriteLine($"Fragment: {uri.Fragment}");
Console.WriteLine($"Host: {uri.Host}");
Console.WriteLine($"HostNameType: {uri.HostNameType}");
Console.WriteLine($"IdnHost: {uri.IdnHost}");
Console.WriteLine($"IsAbsoluteUri: {uri.IsAbsoluteUri}");
Console.WriteLine($"IsDefaultPort: {uri.IsDefaultPort}");
Console.WriteLine($"IsFile: {uri.IsFile}");
Console.WriteLine($"IsLoopback: {uri.IsLoopback}");
Console.WriteLine($"IsUnc: {uri.IsUnc}");
Console.WriteLine($"LocalPath: {uri.LocalPath}");
Console.WriteLine($"OriginalString: {uri.OriginalString}");
Console.WriteLine($"PathAndQuery: {uri.PathAndQuery}");
Console.WriteLine($"Port: {uri.Port}");
Console.WriteLine($"Query: {uri.Query}");
Console.WriteLine($"Scheme: {uri.Scheme}");
Console.WriteLine($"Segments: {string.Join(", ", uri.Segments)}");
Console.WriteLine($"UserEscaped: {uri.UserEscaped}");
Console.WriteLine($"UserInfo: {uri.UserInfo}");

// AbsolutePath: C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/2019/Professional/Common7/IDE/devenv.exe
// AbsoluteUri: file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/2019/Professional/Common7/IDE/devenv.exe
// DnsSafeHost: 
// Fragment: 
// Host: 
// HostNameType: Basic
// IdnHost: 
// IsAbsoluteUri: True
// IsDefaultPort: True
// IsFile: True
// IsLoopback: True
// IsUnc: False
// LocalPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
// OriginalString: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
// PathAndQuery: C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/2019/Professional/Common7/IDE/devenv.exe
// Port: -1
// Query: 
// Scheme: file
// Segments: /, C:/, Program%20Files%20(x86)/, Microsoft%20Visual%20Studio/, 2019/, Professional/, Common7/, IDE/, devenv.exe
// UserEscaped: False
// UserInfo: 

f:id:yotiky:20210325140809p:plain

AbsolutePath は「/」で区切られ、URLエンコードされる。
AbsoluteUriAbsolutePath に file スキームが付く。
LocalPathWindows なら「\」で区切られ、URLデコードされる。

絶対パス
Console.WriteLine($"LocalPath: {uri.LocalPath}");

// LocalPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
相対パスを使って対象の絶対パスを取得
var uri2 = new Uri(uri, @".\..\..\..\..");

Console.WriteLine($"AbsolutePath: {uri.AbsolutePath}");
Console.WriteLine($"AbsolutePath: {uri2.AbsolutePath}");

// AbsolutePath: C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/2019/Professional/Common7/IDE/devenv.exe
// AbsolutePath: C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/

Console.WriteLine($"LocalPath: {uri.LocalPath}");
Console.WriteLine($"LocalPath: {uri2.LocalPath}");

// LocalPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe
// LocalPath: C:\Program Files (x86)\Microsoft Visual Studio\

Console.WriteLine($"AbsoluteUri: {uri.AbsoluteUri}");
Console.WriteLine($"AbsoluteUri: {uri2.AbsoluteUri}");

// AbsoluteUri: file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/2019/Professional/Common7/IDE/devenv.exe
// AbsoluteUri: file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio/

f:id:yotiky:20210325140830p:plain

基準のパスから対象の相対パスを取得
var relativeUri = uri2.MakeRelativeUri(uri);
Console.WriteLine($"MakeRelativeUri: {relativeUri}");

// MakeRelativeUri: 2019/Professional/Common7/IDE/devenv.exe

f:id:yotiky:20210325140924p:plain

参考