2つのパス間の相対パスを取得する(非API)

例えば画像ファイルを一覧できるHTMLファイルを書き出したい場合など、HTMLファイルの位置から見た画像ファイルへの相対パスを知りたいことがある。このようなときはPathRelativePathToを利用する。

...が、この関数は引数として相対パスを格納するバッファサイズが指定できないなど今後変更されそうな雰囲気を匂わせているAPIでもある。そのためここでは手作業で変換する関数を作成した。と、もっともらしく書きたいが実際はコードを書いたときにPathRelativePathToの存在を知らなかっただけだったりもする。開発で利用するならMicrosoftが用意しているので誤動作の心配がないPathRelativePathToを利用した方がいいだろう。

ちなみにこの関数もPathRelativePathToも、相対パスで表現できないパスが与えられた場合は処理に失敗する。

依存環境:ATL
#include "atlstr.h"


//
//	相対パスの取得
//
//Aから見たBへの相対パスを返す。
//ドライブが異なるなど相対パス化できないときは関数は失敗する。
//パスはフルパスで渡すこと!
//一般的にはAPIとして用意されているPathRelativePathToを利用する
//
bool	GetRelatedPath(CAtlString* pstrRelatedPath,LPCTSTR pszPathA,bool bPathAisFolder,LPCTSTR pszPathB,bool bPathBisFolder)
{
	int			nFind;
	CAtlString	strPathA;
	CAtlString	strPathB;

	if(pstrRelatedPath == NULL)
		return	false;
	*pstrRelatedPath = _T("");
	if(pszPathB == NULL || pszPathB == NULL)
		return	false;

	strPathA = pszPathA;
	strPathB = pszPathB;
	if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
		return	false;

	if(bPathAisFolder == false)
	{
		//パスAからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
		nFind = strPathA.ReverseFind(_T('\\'));
		if(nFind < 0)
			return	false;			//どんなファイルパスでも最低1つは「¥」があるはず
		strPathA = strPathA.Left(nFind);
	}

	if(bPathBisFolder == false)
	{
		//パスBからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
		nFind = strPathB.ReverseFind(_T('\\'));
		if(nFind < 0)
			return	false;			//どんなファイルパスでも最低1つは「¥」があるはず
		strPathB = strPathB.Left(nFind);
	}

	if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
		return	false;

	nFind = strPathB.Find(strPathA);
	if(nFind == 0)
	{
		//以下のような関係だった場合
		//PATHA=c:\\aaa\\bbb\\aa.txt
		//PATHB=c:\\aaa\\bbb\\ccc\\aa.txt
		//結果=ccc/aa.txt

		*pstrRelatedPath = pszPathB;
		*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathA.GetLength() - 1);
		return	true;
	}

	while(1)
	{
		//以下のような関係だった場合
		//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
		//PATHB=c:\\aaa\\bbb\\aa.txt
		//結果=../aa.txt
		// ↑この場合はwhileの1度目のループで結果が取得できる
		//
		//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
		//PATHB=c:\\aaa\\bbb\\ddd\\aa.txt
		//結果=../ddd/aa.txt
		// ↑この場合はwhileの2度目のループで結果が取得できる

		nFind = strPathA.Find(strPathB);
		if(nFind == 0)
		{
			*pstrRelatedPath = pszPathB;
			*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathB.GetLength() - 1);

			strPathA = pszPathA;
			strPathA = strPathA.Right(strPathA.GetLength() - strPathB.GetLength() - 1);
			nFind = strPathA.Replace(_T("\\"),_T("\\"));

			int		i;
			for(i = 0; i < nFind; i++)
				*pstrRelatedPath = _T("..\\") + *pstrRelatedPath;

			return	true;
		}

		nFind = strPathB.ReverseFind(_T('\\'));
		if(nFind < 0)
			break;
		strPathB = strPathB.Left(nFind);
	}

	return	false;
}





void	Test(void)
{
	bool		ret;
	CAtlString	strPath;
	ret = GetRelatedPath(&strPath,_T("c:\\aaa\\bbb\\ccc\\aa.txt"),false,_T("c:\\aaa\\bbb\\ddd\\aa.txt"),false);

	if(ret)
		::MessageBox(NULL,strPath,_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

プロジェクトファイルをダウンロード


カテゴリー「ファイル・フォルダ」 のエントリー