相対パス取得(C#)

-- Index --

・Top

・Softwares

▼親父の独り言集

・Oracleデータベースに対する独り言集

▼VB.NETやC#に対する独り言集

・ご利用の前に
・Excel遅延バインディング読み書き高速化(C#)
・Excelデータ比較(C#)
・並列処理グループの直列化(C#)
・Treeコマンドもどき(C#)
・コマンドラインを配列に分割する(C#)
→相対パス取得(C#)
・ListViewのフォーカス行取得(VB.NET)
・DataGridViewでパスワードマスク

・我が家の家電事情について

いきさつ

ある起点となるディレクトリから相対パスを取得するには、どのようにしたらよろしいのでしょうか? いろいろ試行錯誤していたら、APIがあった・・・。なんでPathクラスには用意されていのかな?GetFullPathは用意されているのに・・・。

参考ソース

/// Copyright(c) 2016 pakkin. All Rights Reserved.
/// [改訂履歴]
/// 2016.01.26 作成
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        private static string Rpad(string target, int padLen)
        {
            int len = Encoding.GetEncoding("Shift-Jis").GetByteCount(target);
            if (padLen - len > 0) target += string.Join("", Enumerable.Repeat(" ", padLen - len));
            return target;
        }

        private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern uint FormatMessage(
              [In] uint dwFlags
            , [In] IntPtr lpSource
            , [In] uint dwMessageId
            , [In] uint dwLanguageId
            , [Out] StringBuilder lpBuffer
            , [In] int nSize
            , [In] IntPtr Arguments
        );
        private static string GetLastWind32ErrorMessage()
        {
            uint cd = (uint)Marshal.GetLastWin32Error();
            StringBuilder buff = new StringBuilder(255);
            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, cd, 0, buff, buff.Capacity, IntPtr.Zero);
            return buff.ToString().TrimEnd('\r','\n');
        }
        [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
        private static extern bool PathRelativePathTo(
              [Out] StringBuilder pszPath
            , [In] string pszFrom
            , [In] FileAttributes dwAttrFrom
            , [In] string pszTo
            , [In] FileAttributes dwAttrTo
        );
        private static string GetRelativePath(string baseDir, string destDir)
        {
            StringBuilder sb = new StringBuilder(260);
            bool res = PathRelativePathTo(sb, baseDir, FileAttributes.Directory, destDir, FileAttributes.Directory);
            //別ドライブの場合絶対パスを返したいのであればコメントアウトを解除
            //return res ? sb.ToString() : destDir;
            return res ? sb.ToString() : GetLastWind32ErrorMessage();
        }
        static void Main(string[] args)
        {
            string[,] tests = new string[,] {
                                              { @"C:\TEMP\1", @"c:" }
                                            , { @"C:\TEMP\1", @"c:\" }
                                            , { @"C:\TEMP\1", @"c:\temp" }
                                            , { @"C:\TEMP\1", @"c:\temp\1" }
                                            , { @"C:\TEMP\1", @"c:\temp\1\1" }
                                            , { @"C:\TEMP\1", @"c:\temp\2" }
                                            , { @"C:\TEMP\1", @"c:\temp\2\2" }
                                            , { @"C:\TEMP\1", @"c:\temx" }
                                            , { @"C:\TEMP\1", @"c:\temx\1" }
                                            , { @"C:\TEMP\1", @"c:\temx\1\1" }
                                            , { @"C:\TEMP\1", @"c:\temx\2" }
                                            , { @"C:\TEMP\1", @"c:\temx\2\2" }
                                            , { @"C:\TEMP\1", @"d:\" }
                                            , { @"C:\TEMP\1", @"d:\temp" }
                                            , { @"\\NET\TEMP\1", @"\\net2" }
                                            , { @"\\NET\TEMP\1", @"\\net2\temp\1" }
                                            , { @"\\NET\TEMP\1", @"\\net" }
                                            , { @"\\NET\TEMP\1", @"\\net\temp" }
                                            , { @"\\NET\TEMP\1", @"\\net\temp\1" }
                                            , { @"\\NET\TEMP\1", @"\\net\temp\1\1" }
                                            , { @"\\NET\TEMP\1", @"\\net\temp\2" }
                                            , { @"\\NET\TEMP\1", @"\\net\temp\2\2" }
                                            , { @"\\NET\TEMP\1", @"\\net\temx" }
                                            , { @"\\NET\TEMP\1", @"\\net\temx\1" }
                                            , { @"\\NET\TEMP\1", @"\\net\temx\1\1" }
                                            , { @"\\NET\TEMP\1", @"\\net\temx\2" }
                                            , { @"\\NET\TEMP\1", @"\\net\temx\2\2" }
            };

            Console.WriteLine("起点         ターゲット     GetRelativeDirectory PathRelativePathTo(API)                比較");
            Console.WriteLine("------------ -------------- -------------------- -------------------------------------- ----");
            for( int i = 0; i < tests.GetLength(0); i++)
            {
                string result1 = GetRelativeDirectory(tests[i, 0], tests[i, 1]);
                string result2 = GetRelativePath(tests[i, 0], tests[i, 1]);
                int comp = result1.CompareTo(result2);
                Console.WriteLine("{0,-12} {1,-14} {2,-20} {3} {4}", tests[i, 0], tests[i, 1], result1, Rpad(result2, 38), comp);
            }
            Console.ReadKey();
        }

        private static string GetRelativeDirectory(string baseDir, string destDir)
        {
            // 同一パス→"."
            if (destDir.Equals(baseDir, StringComparison.CurrentCultureIgnoreCase)) return ".";
            // パスを解体
            string[] dd = destDir.Split('\\');
            string[] bd = baseDir.ToLower().Split('\\');
            // 未設定→絶対パス
            if (dd.Length == 0 || bd.Length == 0) return destDir;
            // ネットワークパス個別処理
            if (dd[0].Length == 0) dd[2] = @"\\" + dd[2];
            if (bd[0].Length == 0) bd[2] = @"\\" + bd[2];
            dd = dd.Where(x => x.Length > 0).ToArray();
            bd = bd.Where(x => x.Length > 0).ToArray(); 
            // ドライブ違い→絶対パス
            if (!dd[0].Equals(bd[0], StringComparison.CurrentCultureIgnoreCase)) return destDir;
            // その他→baseからdestまでの経路算出
            var ddi = dd.Select((x, i) => new { vx = x.ToLower(), ix = i });      // 配列番号固定化(ix)
            var ddf = ddi.Where(x => x.vx != (x.ix < bd.Length ? bd[x.ix] : "")); // パス相違配列番号のみ抽出
            int p = ddf.Count() == 0 ? dd.Length : ddf.Select(x => x.ix).Min();   // パス相違開始位置(最小)取得
            string[] up = Enumerable.Repeat("..", bd.Length - p).ToArray();       // baseからの上位階層移動
            if (up.Length == 0) up = new string[] { "." };                        // 上位階層移動無し
            string[] down = dd.Where((x, i) => i >= p).ToArray();                 // 下位階層移動
            return string.Join("\\", up.Concat(down));                            // 結合
        }
    }
}

実行結果

APIの場合、別ドライブはエラーとなる様子。相対パスでは表現できないから当然か。


Copyright(c) 2014-2017 pakkin. All Rights Reserved.