none
指定したフォルダ以下の、フルパスではないファイルパスを取得する方法について RRS feed

  • 質問

  • 指定したフォルダ以下にあるファイルのフルパスを取得する方法はいくらかあるのですが、
    フォルダを指定して、そのフォルダから下のパス情報のみを取得する方法はあるでしょうか?

    例)以下の様なフォルダ構成で、"c:\test"を指定した場合、
    "test.txt","hoge\01.ini","hoge\02.txt"
    c:\test\test.txt
    c:\test\hoge\01.ini
    c:\test\hoge\02.txt

    自分が思いついたのは、フルパスを習得して指定フォルダ部分を置換で消す、なのですが、
    対象となるファイルが数百になりそうなので、あまりにも頭が悪い方法だなと…
    もしそういうメソッドが用意されているのであれば、教えていただけると幸いです。

    2016年8月30日 15:30

回答

  • 特定のフォルダまたはそのサブフォルダにあるというのが絶対条件なら、置換などという面倒なことはせず、"c:\test\" という部分をカットするだけなので、Substring( ベースパス.Length ); すればいいのでは?

    注意点としては、"c:\test" のように、\ で終わっていない場合は、\を追加して文字列長を割り出す必要があるという点ですかね。

    こちらは、

    string base = @"c:\test";
    int skip = base.Length;
    if( base[skip-1] != System.IO.Path.DirectorySeparatorChar )
    {
    	skip++;
    }
    var files = Directory.GetFiles( base, "*" ).Select( s => s.Substring( skip ) );
    

    こんな感じで使えば、サブフォルダだけの相対パスになります。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    • 回答としてマーク no title 2016年9月2日 16:03
    2016年8月31日 5:11

すべての返信

  • こんにちは。

    相対パスから配下のファイルの絶対パスを取得するのではなく、
    相対パスを取得したいのですか?

    Directory.GetFilesなどではFileSystemEnumerableIteratorまで行ってフルパスを文字列で返しているので
    難しそうですね。
    頭が悪い方法と言われたら提示しにくいですが、私はあまり考えずに置換してしまいそうです…。

    var relativeFiles = Directory.GetFiles(@"C:\test", "*", SearchOption.AllDirectories)
                        .Select(s => s.Replace(@"C:\test\", string.Empty));
    
    MakeRelativeUriなどを使って相対パスを取得する方法はありますが、どちらにせよ絶対パスから相対パスへひとつづつ変換することには
    なるのではないかなと思います。

    2016年8月30日 16:09
    モデレータ
  • お早い回答ありがとうございます。
    そうですね、回りくどい説明をせずに、相対パスが取得したいといえばよかったです…

    やはり置換が手っ取り早い方法なのですかね、もし他に方法がなければその方向で考えたいと思います!

    2016年8月30日 16:31
  • 回答ではなくもしかして参考になる?程度の投稿ですが、

    Win32APIのFindFirstFileはカレントディレクトリからの相対パス指定ができるので使えるかもしれません。

    下記はC++で試したコードです。(.NETでなくてすみません)

    #pragma comment(lib,"Shlwapi")
    #include <windows.h>
    #include <shlwapi.h>
    #include <iostream>
    
    void myfind(char* lpszDir)
    {
    	char szPath[MAX_PATH];
    	strcpy(szPath, lpszDir);
    	PathAppend(szPath, "*.*");
    	WIN32_FIND_DATA fd;
    	HANDLE hSearch = FindFirstFile(szPath, &fd);
    	if (hSearch == INVALID_HANDLE_VALUE) {
    		return;
    	}
    	for (;;) {
    		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    			if (strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")) {
    				strcpy(szPath, lpszDir);
    				PathAppend(szPath, fd.cFileName);
    				myfind(szPath);
    			}
    		}
    		else {
    			strcpy(szPath, lpszDir);
    			PathAppend(szPath, fd.cFileName);
    			std::cout << szPath << std::endl;
    		}
    		if (!FindNextFile(hSearch, &fd)) {
    			break;
    		}
    	}
    	FindClose(hSearch);
    }
    
    int main()
    {
    	char szCurrentPath[MAX_PATH];
    	GetCurrentDirectory(MAX_PATH, szCurrentPath);
    	SetCurrentDirectory("c:\\test");
    	myfind("");
    	SetCurrentDirectory(szCurrentPath);
    	return 0;
    }

    長いですが^^; 絶対パスから相対パスへの文字列の変換は必要ないです。
    ※myfind 関数の使用時にカレントディレクトリを検索するフォルダに変更し終了時に戻しています。
    ※再帰を使っているので検索するフォルダのフォルダ階層が深い場合(沢山再帰する場合)は注意です。


    2016年8月30日 17:27
  • Pathクラスにそのような機能は用意されていませんね。少し異なりますがUriクラスに近い機能が用意されています。

    var path = new Uri(@"c:\test\hoge\01.ini");
    var dir = new Uri(@"c:\test\");
    Console.WriteLine(dir.MakeRelativeUri(path));  // "hoge/01.ini"

    PathでなくUriなのでディレクトリ区切りが / になってしまいます。

    2016年8月30日 23:04
  • Win32APIのPathRelativePathTo()でも可能です。

    https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb773740(v=vs.85).aspx

    こちらで、様々な方法が記載されていました。

    http://dobon.net/vb/dotnet/file/getabsolutepath.html

    いずれの方法も、一旦絶対パスを取得して相対パスに変換していますので、

    置換とあまり変わらないと言われればその通りかもしれませんが…。

    2016年8月31日 0:57
  • 置換以外となると、以下のようにベタに書く方法もあります。
    以下のコードではListBoxに結果を追加しています。

    private void button1_Click(object sender, EventArgs e)
    {
        AddToListBox(@"c:\work", "");
    }
    
    private void AddToListBox(string directoryName, string prefix)
    {
        IEnumerable<string> files = Directory.EnumerateFileSystemEntries(
                                    directoryName, "*");
    
        foreach(string f in files)
        {
            if(Directory.Exists(f))
                AddToListBox(f, Path.GetFileName(f) + @"\");
            else
                listBox1.Items.Add(prefix + Path.GetFileName(f));
        }
    }


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年8月31日 1:43
    モデレータ
  • 特定のフォルダまたはそのサブフォルダにあるというのが絶対条件なら、置換などという面倒なことはせず、"c:\test\" という部分をカットするだけなので、Substring( ベースパス.Length ); すればいいのでは?

    注意点としては、"c:\test" のように、\ で終わっていない場合は、\を追加して文字列長を割り出す必要があるという点ですかね。

    こちらは、

    string base = @"c:\test";
    int skip = base.Length;
    if( base[skip-1] != System.IO.Path.DirectorySeparatorChar )
    {
    	skip++;
    }
    var files = Directory.GetFiles( base, "*" ).Select( s => s.Substring( skip ) );
    

    こんな感じで使えば、サブフォルダだけの相対パスになります。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    • 回答としてマーク no title 2016年9月2日 16:03
    2016年8月31日 5:11
  • 回答ありがとうございます。
    substringが直感的に分かりやすかったため、今回はこちらで解決とさせていただきます!
    他の方法も良かったのですが、処理の内容が目に見えて分かりやすいというのも大きかったです…
    2016年9月2日 16:04