DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

相対パスから絶対パスを取得する、絶対パスから相対パスを取得する

ここでは、ファイルやフォルダの相対パスから絶対パス(フルパス)を取得する方法と、絶対パスから相対パスを取得する方法を幾つか紹介します。

なお、例えば「C:\Windows\」というフォルダにある「file.txt」というファイルの絶対パス「C:\Windows\file.txt」を取得したいというように、両者を文字列としてつなぐだけでよいのであれば、「フォルダ名とファイル名を結合して、絶対パスを作成する」をご覧ください。

現在のディレクトリを基準にして相対パスから絶対パスを取得する

現在のディレクトリを基準にして相対パスから絶対パスを取得するには、Path.GetFullPathメソッドを使用します。なお現在のディレクトリを変更する方法は、「カレントディレクトリ(現在の作業ディレクトリ)を取得、設定する」をご覧ください。

VB.NET
コードを隠すコードを選択
'カレントディレクトリを変更
System.Environment.CurrentDirectory = "C:\Windows\System"
'相対パス"..\file.txt"の絶対パスを取得する
Console.WriteLine(System.IO.Path.GetFullPath("..\file.txt"))
'結果: C:\Windows\file.txt
C#
コードを隠すコードを選択
//カレントディレクトリを変更
System.Environment.CurrentDirectory = "C:\\Windows\\System";
//相対パス"..\file.txt"の絶対パスを取得する
Console.WriteLine(System.IO.Path.GetFullPath("..\\file.txt"));
//結果: C:\Windows\file.txt

Uriクラスを使用する方法

相対URLから絶対URLを取得する、絶対URLから相対URLを取得する」で紹介しているのと同じように、Uriクラスを使って相対パスと絶対パスの変換を行うことができます。ただ、後で説明するように少し癖があるため、細かい点を気にすると意外と面倒です。

任意のディレクトリを基準にして相対パスから絶対パスを取得する

相対URLから絶対URLを取得する」と基本的には同じですが、パスの取得にはUri.LocalPathプロパティを使うのが良いでしょう。そうしないと、パスの区切り文字が「\」ではなく「/」になり、一部の文字はURLエンコードされたままになってしまいます。

「C:\Windows\System\」を基準とした相対パス「..\file.txt」の絶対パスを取得する例を以下に示します。

VB.NET
コードを隠すコードを選択
'"C:\Windows\System\"を基準とした"..\file.txt"の絶対パスを取得
Dim u1 As New Uri("C:\Windows\System\")
Dim u2 As New Uri(u1, "..\file.txt")
Console.WriteLine(u2.LocalPath)
'結果: C:\Windows\file.txt
C#
コードを隠すコードを選択
//"C:\Windows\System\"を基準とした"..\file.txt"の絶対パスを取得
Uri u1 = new Uri("C:\\Windows\\System\\");
Uri u2 = new Uri(u1, "..\\file.txt");
Console.WriteLine(u2.LocalPath);
//結果: C:\Windows\file.txt

基準パスと相対パスをいろいろと変更して、どのように絶対パスが取得されるかを調べた結果を以下に示します。

基準となるパス 相対パス 取得される絶対パス
C:\Windows\System\ a\b.txt C:\Windows\System\a\b.txt
C:\Windows\System\ ..\..\a\b.txt C:\a\b.txt
C:\Windows\System\ \a\b.txt \\a\b.txt
C:\Windows\System\ .\b.txt C:\Windows\System\b.txt
C:\Windows\System\ D:\a\ D:\a\
C:\Windows\System a\b.txt C:\Windows\a\b.txt

URLデコードされないようにする

Uriクラスで厄介なのは、勝手にURLエンコードとデコードがされる点です。これについて詳しくは、「Uriコンストラクタで行われるURLエンコードとデコードについて」で説明しています。

上記の例のようにUri.LocalPathプロパティを使って絶対パスを取得した場合は、LocalPathプロパティがURLデコードしてくれるため、URLエンコードの心配はありません。

しかし、デコードはされます。例えば上記の例で相対パスを「..\%41.txt」にして絶対パスを取得すると、そのパスは「C:\Windows\A.txt」(「%41」が「A」にデコードされていることに注目してください)になってしまいます。

これを防ぐ方法としては、事前に「%」を「%25」にエンコードしておく方法が考えられます。この方法により、先ほどのコードを改良した例を示します。

VB.NET
コードを隠すコードを選択
Dim basePath As String = "C:\%25\System\"
Dim filePath As String = "..\%41#.txt"

'"%"を"%25"に変換しておく(デコード対策)
basePath = basePath.Replace("%", "%25")
filePath = filePath.Replace("%", "%25")

'絶対パスを取得する
Dim u1 As New Uri(basePath)
Dim u2 As New Uri(u1, filePath)
Dim absolutePath As String = u2.LocalPath
'"%25"を"%"に戻す
absolutePath = absolutePath.Replace("%25", "%")

'結果を表示する
Console.WriteLine(absolutePath)
' C:\%25\%41#.txt
C#
コードを隠すコードを選択
string basePath = "C:\\%25\\System\\";
string filePath = "..\\%41#.txt";

//"%"を"%25"に変換しておく(デコード対策)
basePath = basePath.Replace("%", "%25");
filePath = filePath.Replace("%", "%25");

//絶対パスを取得する
Uri u1 = new Uri(basePath);
Uri u2 = new Uri(u1, filePath);
string absolutePath = u2.LocalPath;
//"%25"を"%"に戻す
absolutePath = absolutePath.Replace("%25", "%");

//結果を表示する
Console.WriteLine(absolutePath);
// C:\%25\%41#.txt

任意のディレクトリを基準にして絶対パスから相対パスを取得する

これも「絶対URLから相対URLを取得する」と同じ方法でできます。ただし、Uri.ToStringメソッドで相対パスを取得すると区切り文字は'\'ではなく'/'になります。

「C:\Windows\System\」を基準とした時の「C:\Windows\file.txt」の相対パスを取得する例を以下に示します。

VB.NET
コードを隠すコードを選択
'"C:\Windows\System\"を基準とした"C:\Windows\file.txt"の相対Uriを取得する
Dim u1 As New Uri("C:\Windows\System\")
Dim u2 As New Uri("C:\Windows\file.txt")

'絶対Uriから相対Uriを取得する
Dim relativeUri As Uri = u1.MakeRelativeUri(u2)
'文字列に変換する
Dim relativePath As String = relativeUri.ToString()
'.NET Framework 1.1以前では次のようにする
'string relativePath = u1.MakeRelative(u2);

'"/"を"\"に変換する
relativePath = relativePath.Replace("/"c, "\"c)

'結果を表示する
Console.WriteLine(relativePath)
'結果: ..\file.txt
C#
コードを隠すコードを選択
//"C:\Windows\System\"を基準とした"C:\Windows\file.txt"の相対Uriを取得する
Uri u1 = new Uri("C:\\Windows\\System\\");
Uri u2 = new Uri("C:\\Windows\\file.txt");

//絶対Uriから相対Uriを取得する
Uri relativeUri = u1.MakeRelativeUri(u2);
//文字列に変換する
string relativePath = relativeUri.ToString();
//.NET Framework 1.1以前では次のようにする
//string relativePath = u1.MakeRelative(u2);

//"/"を"\"に変換する
relativePath = relativePath.Replace('/', '\\');

//結果を表示する
Console.WriteLine(relativePath);
//結果: ..\file.txt

基準パスと絶対パスをいろいろと変更して、どのように相対パスが取得されるかを調べた結果を以下に示します。

基準となるパス 絶対パス 取得される相対パス
C:\Windows\System\ C:\a\b.txt ..\..\a\b.txt
C:\a\b.txt C:\Windows\System\ ..\Windows\System\
C:\Windows\System C:\a\b.txt ..\a\b.txt
C:\a\b.txt C:\Windows\System ..\Windows\System
C:\Windows\System\ D:\a\ D:\a\
D:\a\ C:\Windows\System\ C:\Windows\System\

URLエンコード、デコードされないようにする

絶対パスを取得する時と同様に、相対パスを取得する場合でもUriクラスが勝手に行なうURLデコード対策が必要です。さらに、Uri.ToStringメソッドはLocalPathプロパティと違って一部の文字をデコードしませんので、エンコードされたパスをデコードする処理も必要になります。

パスをURLデコードする時に注意しなければならないのは、「+」を半角スペースに変換してはいけない点です。「URLエンコード、URLデコードを行う」で説明しているように、Uri.UnescapeDataStringメソッドは「+」を変換しませんが、HttpUtility.UrlDecodeメソッドは変換しますので、パスのデコードにはUnescapeDataStringメソッドを使います。ただし、.NET Framework 1.1以前などでどうしてもUrlDecodeメソッドを使わなければならない場合は、あらかじめ「+」を「%2B」に変換しておくなどの処置が必要になります。

VB.NET
コードを隠すコードを選択
Dim basePath As String = "C:\Windows\System\"
Dim filePath As String = "C:\Windows\%41#.txt"

'"%"を"%25"に変換しておく(デコード対策)
basePath = basePath.Replace("%", "%25")
filePath = filePath.Replace("%", "%25")

'相対パスを取得する
Dim u1 As New Uri(basePath)
Dim u2 As New Uri(filePath)
Dim relativeUri As Uri = u1.MakeRelativeUri(u2)
Dim relativePath As String = relativeUri.ToString()
'.NET Framework 1.1以前では次のようにする
'string relativePath = u1.MakeRelative(u2);

'URLデコードする(エンコード対策)
relativePath = Uri.UnescapeDataString(relativePath)
'.NET Framework 1.1以前では次のようにする
'relativePath = System.Web.HttpUtility.UrlDecode(
'    relativePath.Replace("+", "%2B"));

'"%25"を"%"に戻す
relativePath = relativePath.Replace("%25", "%")

'結果を表示する
Console.WriteLine(relativePath)
' ../%41#.txt
C#
コードを隠すコードを選択
string basePath = "C:\\Windows\\System\\";
string filePath = "C:\\Windows\\%41#.txt";

//"%"を"%25"に変換しておく(デコード対策)
basePath = basePath.Replace("%", "%25");
filePath = filePath.Replace("%", "%25");

//相対パスを取得する
Uri u1 = new Uri(basePath);
Uri u2 = new Uri(filePath);
Uri relativeUri = u1.MakeRelativeUri(u2);
string relativePath = relativeUri.ToString();
//.NET Framework 1.1以前では次のようにする
//string relativePath = u1.MakeRelative(u2);

//URLデコードする(エンコード対策)
relativePath = Uri.UnescapeDataString(relativePath);
//.NET Framework 1.1以前では次のようにする
//relativePath = System.Web.HttpUtility.UrlDecode(
//    relativePath.Replace("+", "%2B"));

//"%25"を"%"に戻す
relativePath = relativePath.Replace("%25", "%");

//結果を表示する
Console.WriteLine(relativePath);
// ../%41#.txt

パスを正規化することで、相対パスから絶対パスを取得する

例えば「C:\Windows\System\..\file.txt」のように「..」や「.」が含まれるパスを、「..」や「.」が含まれないパスに正規化することで、相対パスを絶対パスに変換することができます。ただし、「..」や「.」以外の表現が使われている相対パスに対しては、この方法は使えません。パスを正規化する方法については、「パス内の"."や".."を削除して、正規化する」をご覧ください。

VB.NET
コードを隠すコードを選択
Dim basePath As String = "C:\Windows\System"
Dim relativePath As String = "..\file.txt"

'基準パスと相対パスをPath.CombineでつなげたものをFileInfoクラスで正規化する
Dim fi As New System.IO.FileInfo(System.IO.Path.Combine(basePath, relativePath))
'絶対パスを取得する
Dim absolutePath As String = fi.FullName

Console.WriteLine(absolutePath)
'結果: C:\Windows\file.txt
C#
コードを隠すコードを選択
string basePath = "C:\\Windows\\System";
string relativePath = "..\\file.txt";

//基準パスと相対パスをPath.CombineでつなげたものをFileInfoクラスで正規化する
System.IO.FileInfo fi = new System.IO.FileInfo(
    System.IO.Path.Combine(basePath, relativePath));
//絶対パスを取得する
string absolutePath = fi.FullName;

Console.WriteLine(absolutePath);
//結果: C:\Windows\file.txt

Win32 APIを使用する方法

PathCombine関数を使用して、相対パスから絶対パスを取得する

PathCombine functionを使うと、相対パスから絶対パスを取得することができます。この関数や、後で紹介するPathRelativePathTo関数は、Windows 2000(もしくは、Windows NT with Internet Explorer 4.0以上)、Windows 98(もしくは、Windows 95 with Internet Explorer 4.0以上)で使用できます。

相対パスから絶対パスを取得するメソッドの例を示します。このメソッドは、「pinvoke.net: PathCombine (shlwapi)」と「FolderBrowserDialog and P/Invoke shlwapi PathCombine cause RtlHeap corruption」を参考にして書きました。

VB.NET
コードを隠すコードを選択
'Imports System.Text
'Imports System.Runtime.InteropServices

<DllImport("shlwapi.dll", _
    CharSet:=CharSet.Auto)> _
Private Shared Function PathCombine( _
    <Out> lpszDest As StringBuilder, _
    lpszDir As String, lpszFile As String) As IntPtr
End Function

''' <summary>
''' 相対パスから絶対パスを取得します。
''' </summary>
''' <param name="basePath">基準とするパス。</param>
''' <param name="relativePath">相対パス。</param>
''' <returns>絶対パス。</returns>
Public Shared Function GetAbsolutePath(basePath As String, _
                                       relativePath As String) As String
    Dim sb As New StringBuilder(2048)
    PathCombine(sb, basePath, relativePath)
    Return sb.ToString()
End Function
C#
コードを隠すコードを選択
//using System.Text;
//using System.Runtime.InteropServices;

[DllImport("shlwapi.dll",
    CharSet = CharSet.Auto)]
private static extern IntPtr PathCombine(
    [Out] StringBuilder lpszDest,
    string lpszDir,
    string lpszFile);

/// <summary>
/// 相対パスから絶対パスを取得します。
/// </summary>
/// <param name="basePath">基準とするパス。</param>
/// <param name="relativePath">相対パス。</param>
/// <returns>絶対パス。</returns>
public static string GetAbsolutePath(string basePath, string relativePath)
{
    StringBuilder sb = new StringBuilder(2048);
    IntPtr res = PathCombine(sb, basePath, relativePath);
    if (res == IntPtr.Zero)
    {
        throw new Exception("絶対パスの取得に失敗しました。");
    }
    return sb.ToString();
}

PathRelativePathTo関数を使用して、絶対パスから相対パスを取得する

PathRelativePathTo functionを使うと、絶対パスから相対パスを取得することができます。

以下に、絶対パスから相対パスを取得するメソッドの例を示します。このコードは、「pinvoke.net: pathrelativepathto (shlwapi)」を参考にして書きました。

VB.NET
コードを隠すコードを選択
'Imports System.Text
'Imports System.Runtime.InteropServices

<DllImport("shlwapi.dll", CharSet:=CharSet.Auto)> _
Private Shared Function PathRelativePathTo( _
    <Out> pszPath As StringBuilder, _
    <[In]> pszFrom As String, _
    <[In]> dwAttrFrom As System.IO.FileAttributes, _
    <[In]> pszTo As String, _
    <[In]> dwAttrTo As System.IO.FileAttributes) As Boolean
End Function

''' <summary>
''' 絶対パスから相対パスを取得します。
''' </summary>
''' <param name="basePath">基準とするフォルダのパス。</param>
''' <param name="absolutePath">相対パス。</param>
''' <returns>絶対パス。</returns>
Public Shared Function GetRelativePath(basePath As String, _
        absolutePath As String) As String
    Dim sb As New StringBuilder(260)
    Dim res As Boolean = PathRelativePathTo( _
        sb, basePath, System.IO.FileAttributes.Directory, _
        absolutePath, System.IO.FileAttributes.Normal)
    If Not res Then
        Throw New Exception("相対パスの取得に失敗しました。")
    End If
    Return sb.ToString()
End Function
C#
コードを隠すコードを選択
//using System.Text;
//using System.Runtime.InteropServices;

[DllImport("shlwapi.dll",
    CharSet = CharSet.Auto)]
private static extern bool PathRelativePathTo(
     [Out] StringBuilder pszPath,
     [In] string pszFrom,
     [In] System.IO.FileAttributes dwAttrFrom,
     [In] string pszTo,
     [In] System.IO.FileAttributes dwAttrTo
);

/// <summary>
/// 絶対パスから相対パスを取得します。
/// </summary>
/// <param name="basePath">基準とするフォルダのパス。</param>
/// <param name="absolutePath">相対パス。</param>
/// <returns>絶対パス。</returns>
public static string GetRelativePath(string basePath, string absolutePath)
{
    StringBuilder sb = new StringBuilder(260);
    bool res = PathRelativePathTo(sb,
        basePath, System.IO.FileAttributes.Directory,
        absolutePath, System.IO.FileAttributes.Normal);
    if (!res)
    {
        throw new Exception("相対パスの取得に失敗しました。");
    }
    return sb.ToString();
}

自分でメソッドを書く

蛇足ですが、上記の方法を使用せずに、自分でメソッドを書いてみました。かなり大雑把で、細かいことは無視していますので、参考程度にしてください。

VB.NET
コードを隠すコードを選択
Imports System.Text
Imports System.IO

Public Class PathEx
    Private Shared ReadOnly DirectorySeparatorChar As Char = _
        Path.DirectorySeparatorChar
    Private Shared ReadOnly CurrentDirectoryString As String = _
        "." + DirectorySeparatorChar
    Private Shared ReadOnly ParentDirectoryString As String = _
        ".." + DirectorySeparatorChar
    Private Shared ReadOnly RootDirectoryString As String = _
        DirectorySeparatorChar.ToString()

    ''' <summary>
    ''' 相対パスから絶対パスを取得します。
    ''' </summary>
    ''' <param name="basePath">基準とするフォルダのパス。</param>
    ''' <param name="relativePath">相対パス。</param>
    ''' <returns>絶対パス。</returns>
    Public Shared Function GetAbsolutePath(basePath As String, _
                                           relativePath As String) As String
        If basePath Is Nothing OrElse basePath.Length = 0 Then
            Return relativePath
        End If
        If relativePath Is Nothing OrElse relativePath.Length = 0 Then
            Return basePath
        End If

        basePath = basePath.TrimEnd(DirectorySeparatorChar)

        If relativePath.StartsWith(ParentDirectoryString) Then
            '相対パスが"..\"で始まっている時
            '基準パスの最後の"\"から後ろを削除する
            Dim pos As Integer = basePath.LastIndexOf(DirectorySeparatorChar)
            If pos < 0 Then
                Throw New ArgumentException("""..\""が多すぎます。", _
                                            "relativePath")
            End If
            basePath = basePath.Remove(pos)
            '相対パスのはじめの"..\"を削除する
            relativePath = relativePath.Substring(ParentDirectoryString.Length)
            'あらためて絶対パスを取得する
            Return GetAbsolutePath(basePath, relativePath)
        ElseIf relativePath.StartsWith(CurrentDirectoryString) Then
            '相対パスが".\"で始まっている時
            '相対パスのはじめの".\"を削除する
            relativePath = relativePath.Substring(CurrentDirectoryString.Length)
            'あらためて絶対パスを取得する
            Return GetAbsolutePath(basePath, relativePath)
        ElseIf relativePath.StartsWith(RootDirectoryString) Then
            '相対パスが"\"で始まっている時
            '基準パスのルートパスを取得する
            basePath = Path.GetPathRoot(basePath)
            basePath = basePath.TrimEnd(DirectorySeparatorChar)
            '相対パスのはじめの"\"を削除する
            relativePath = relativePath.Substring(RootDirectoryString.Length)
        End If

        'パスを連結する
        Return basePath & DirectorySeparatorChar & relativePath
    End Function

    ''' <summary>
    ''' 絶対パスから相対パスを取得します。
    ''' </summary>
    ''' <param name="basePath">基準とするフォルダのパス。</param>
    ''' <param name="absolutePath">絶対パス。</param>
    ''' <returns>相対パス。</returns>
    Public Shared Function GetRelativePath(basePath As String, _
                                           absolutePath As String) As String
        If basePath Is Nothing OrElse basePath.Length = 0 Then
            Return absolutePath
        End If
        If absolutePath Is Nothing OrElse absolutePath.Length = 0 Then
            Return ""
        End If

        basePath = basePath.TrimEnd(DirectorySeparatorChar)

        'パスを"\"で分割する
        Dim basePathDirs As String() = _
            basePath.Split(DirectorySeparatorChar)
        Dim absolutePathDirs As String() = _
            absolutePath.Split(DirectorySeparatorChar)

        '基準パスと絶対パスで、先頭から共通する部分を探す
        Dim commonCount As Integer = 0
        While commonCount < basePathDirs.Length AndAlso _
            commonCount < absolutePathDirs.Length AndAlso _
            basePathDirs(commonCount).Equals(absolutePathDirs(commonCount), _
                StringComparison.OrdinalIgnoreCase)
            '共通部分の数を覚えておく
            commonCount += 1
        End While

        '共通部分がない時
        If commonCount = 0 Then
            Return absolutePath
        End If

        '共通部分以降の基準パスのフォルダの深さを取得する
        Dim baseOnlyCount As Integer = basePathDirs.Length - commonCount
        'その数だけ"..\"を付ける
        Dim buf As New StringBuilder()
        For i As Integer = 0 To baseOnlyCount - 1
            buf.Append(ParentDirectoryString)
        Next

        '共通部分以降の絶対パス部分を追加する
        buf.Append(String.Join(DirectorySeparatorChar.ToString(), _
                               absolutePathDirs, commonCount, _
                               absolutePathDirs.Length - commonCount))

        Return buf.ToString()
    End Function
End Class
C#
コードを隠すコードを選択
using System;
using System.Text;
using System.IO;

public class PathEx
{
    private readonly static char DirectorySeparatorChar =
        Path.DirectorySeparatorChar;
    private readonly static string CurrentDirectoryString =
        "." + DirectorySeparatorChar;
    private readonly static string ParentDirectoryString =
        ".." + DirectorySeparatorChar;
    private readonly static string RootDirectoryString =
        DirectorySeparatorChar.ToString();

    /// <summary>
    /// 相対パスから絶対パスを取得します。
    /// </summary>
    /// <param name="basePath">基準とするフォルダのパス。</param>
    /// <param name="relativePath">相対パス。</param>
    /// <returns>絶対パス。</returns>
    public static string GetAbsolutePath(string basePath, string relativePath)
    {
        if (basePath == null || basePath.Length == 0)
        {
            return relativePath;
        }
        if (relativePath == null || relativePath.Length == 0)
        {
            return basePath;
        }

        basePath = basePath.TrimEnd(DirectorySeparatorChar);

        if (relativePath.StartsWith(ParentDirectoryString))
        {
            //相対パスが"..\"で始まっている時
            //基準パスの最後の"\"から後ろを削除する
            int pos = basePath.LastIndexOf(DirectorySeparatorChar);
            if (pos < 0)
            {
                throw new ArgumentException(
                    "\"..\\\"が多すぎます。", "relativePath");
            }
            basePath = basePath.Remove(pos);
            //相対パスのはじめの"..\"を削除する
            relativePath = relativePath.Substring(ParentDirectoryString.Length);
            //あらためて絶対パスを取得する
            return GetAbsolutePath(basePath, relativePath);
        }
        else if (relativePath.StartsWith(CurrentDirectoryString))
        {
            //相対パスが".\"で始まっている時
            //相対パスのはじめの".\"を削除する
            relativePath = relativePath.Substring(CurrentDirectoryString.Length);
            //あらためて絶対パスを取得する
            return GetAbsolutePath(basePath, relativePath);
        }
        else if (relativePath.StartsWith(RootDirectoryString))
        {
            //相対パスが"\"で始まっている時
            //基準パスのルートパスを取得する
            basePath = Path.GetPathRoot(basePath);
            basePath = basePath.TrimEnd(DirectorySeparatorChar);
            //相対パスのはじめの"\"を削除する
            relativePath = relativePath.Substring(RootDirectoryString.Length);
        }

        //パスを連結する
        return basePath + DirectorySeparatorChar + relativePath;
    }

    /// <summary>
    /// 絶対パスから相対パスを取得します。
    /// </summary>
    /// <param name="basePath">基準とするフォルダのパス。</param>
    /// <param name="absolutePath">絶対パス。</param>
    /// <returns>相対パス。</returns>
    public static string GetRelativePath(string basePath, string absolutePath)
    {
        if (basePath == null || basePath.Length == 0)
        {
            return absolutePath;
        }
        if (absolutePath == null || absolutePath.Length == 0)
        {
            return "";
        }

        basePath = basePath.TrimEnd(DirectorySeparatorChar);

        //パスを"\"で分割する
        string[] basePathDirs = basePath.Split(DirectorySeparatorChar);
        string[] absolutePathDirs = absolutePath.Split(DirectorySeparatorChar);

        //基準パスと絶対パスで、先頭から共通する部分を探す
        int commonCount = 0;
        for (int i = 0;
            i < basePathDirs.Length &&
            i < absolutePathDirs.Length &&
            basePathDirs[i].Equals(absolutePathDirs[i],
                StringComparison.OrdinalIgnoreCase);
            i++)
        {
            //共通部分の数を覚えておく
            commonCount++;
        }

        //共通部分がない時
        if (commonCount == 0)
        {
            return absolutePath;
        }

        //共通部分以降の基準パスのフォルダの深さを取得する
        int baseOnlyCount = basePathDirs.Length - commonCount;
        //その数だけ"..\"を付ける
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < baseOnlyCount; i++)
        {
            buf.Append(ParentDirectoryString);
        }

        //共通部分以降の絶対パス部分を追加する
        buf.Append(string.Join(DirectorySeparatorChar.ToString(),
            absolutePathDirs,
            commonCount, absolutePathDirs.Length - commonCount));

        return buf.ToString();
    }
}
  • 履歴:
  • 2006/11/20 表を追加。
  • 2007/1/25 .NET Framework 2.0に関する記述を追加。
  • 2007/9/22 相対パスがURLエンコードされることを追記。
  • 2013/6/2 「任意のディレクトリを基準にして絶対パスから相対パスを取得する」でURLデコードする方法を変更(コメントのご指摘を参考にさせていただきました)。「Uriクラスを使用する方法」で、URLエンコード、デコードに関する説明を補充。「パスを正規化することで、相対パスから絶対パスを取得する」と「Win32 APIを使用する方法」と「自分でメソッドを書く」を追加。
  • 2016/9/4 誤記修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。