最近在資料夾比對工具遇到一個需求:要以某個資料夾為基準推算檔案的相對路徑。例如,若基準資料夾為 C:\Folder,則 C:\Folder\SubFolder\Test.txt 的相對路徑為 SubFolder\Test.txt。
類似需求以前加減寫過,說穿了全是字串比對的功夫,靠將路徑前方相同部分移除取得相對路徑。如果相對路徑需支援 ..\..\Test.txt 這種寫法,邏輯會複雜一些,若還再考慮英文大小寫的話...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace UriTest
{
class Program
{
static void Main(string[] args)
{
//標準測試
Test(@"C:\Folder\Test.txt", @"C:\Folder\");
Test(@"C:\Folder\SubFolder\Test.txt", @"C:\Folder\");
//上層資料夾
Test(@"C:\Folder\Test.txt", @"C:\Folder\SubFolder");
//相鄰資料夾
Test(@"C:\Folder\SubFolderA\Test.txt", @"C:\Folder\SubFolderB");
//忽略大小寫差異
Test(@"c:\folder\subfolder\Test.txt", @"C:\FOLDER\SUBFOLDER");
//限定basePath子目錄下,不允許上層或相鄰目錄(安全考量)
try
{
Test(@"C:\Folder\Test.txt", @"C:\Folder\SubFolder", true);
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
try
{
Test(@"C:\Folder\SubFolderA\Test.txt", @"C:\Folder\SubFolderB", true);
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
Console.ReadLine();
}
static void Test(string path, string basePath, bool limitSubfolder = false)
{
Console.WriteLine(new string('=', 40));
Console.WriteLine($"Path: {path}");
Console.WriteLine($"Base Path: {basePath}");
Console.WriteLine(
$"Relative Path: {GetRelativePath(path, basePath, limitSubfolder)}");
}
public static string GetRelativePath(string fullPath, string basePath,
bool limitSubfolder = false)
{
if (!basePath.EndsWith(@"\")) basePath += @"\";
Uri fp = new Uri(fullPath);
Uri bp = new Uri(basePath);
var relPath = bp.MakeRelativeUri(fp).ToString().Replace("/", @"\");
if (relPath.Contains(@"..\") && limitSubfolder)
throw new ApplicationException("path must be under basePath!");
return relPath;
}
}
}