CODE - C# 推算檔案相對路徑

最近在資料夾比對工具遇到一個需求:要以某個資料夾為基準推算檔案的相對路徑。例如,若基準資料夾為 C:\Folder,則 C:\Folder\SubFolder\Test.txt 的相對路徑為 SubFolder\Test.txt。

類似需求以前加減寫過,說穿了全是字串比對的功夫,靠將路徑前方相同部分移除取得相對路徑。如果相對路徑需支援 ..\..\Test.txt 這種寫法,邏輯會複雜一些,若還再考慮英文大小寫的話...

總之,計算相對路徑說難不難,自己寫瑣碎細節倒也不少。於是我想起前幾天談到的 .NET Uri 類別,恰巧有個 MakeRelativeUri() 方法,研究後找到坐享其成的省事解法。

直接用範例程式展示:

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;
        }
    }
}

結果如下,只需幾行程式搞定,推算"..\"上層目錄、英文大小寫有別都難不倒它。

補充兩個小地方:

  1. Uri 傳回的路徑依循 RFC 規範用"/"作為目錄分隔字元,在 Windows 環境可將"/"置換成"\"以求一致。
  2. 實務上有時會限制檔案必須放在指定目錄下,因此我在 GetRelativePath() 加了 limitSubfoler 參數,以便在跳脫範圍時抛出例外。
歡迎推文分享:
Published 13 March 2018 09:35 PM 由 Jeffrey
Filed under:
Views: 3,255



意見

沒有意見

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<March 2018>
SunMonTueWedThuFriSat
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication