こんにちは、Rayです。
今回は、MATLABで相対パスを絶対パスに変換する方法を紹介します。
相対パスは便利なのでMATLABでもよく使いますが、絶対パスが欲しくなることもあります。
こんなとき、
あー、相対パスを絶対パスに変換できたらいいのになぁ
と思うのですが、残念ながらこの目的に合うMATLAB標準の関数はありません(多分)。
というわけで、この目的を満たす自作の関数を作成しました。
コードを公開した上で、簡単に解説も加えます。
特段難しい物でもないですが、誰かの役に立てばいいなと思います。
目的
今回の目的は、MATLABを使って、相対パス( ../../folderA )の文字列を絶対パス(/Users/name/folderA)に変換することです。
この要求をストレートに満たしてくれる関数はMATLABには存在しないようなので、関数を自作してみました。
細かいことはいいから関数を見たい!という方は「自作関数(rel2abs)」の項をご覧ください。
想定する場面
次のようなディレクトリ構造を考えてみます。
(コンピュータはMac、ユーザー名はray(私の名前)としています。)
(ただし、今回紹介する自作関数はWindowsでも使用可能です。)
現在MATLABでは、
/Users/ray/Experiments/Exp2
をcurrent directoryとして、実験2のデータを解析しています。
諸事情により、
/Users/ray/Analysis
にscriptsフォルダを作成し、そこにprocessing.mファイルを作成しようと思ってます。
これまた諸事情により、このフォルダー・ファイルを作成する前に、このファイルの絶対パスを欲しています。
諸事情って人それぞれあると思うんですよ。
みなさんの諸事情に置き換えて考えてください!(投げやり)
絶対パスでなく相対パスを使いたい場面については、こちらの記事でも触れているので気になる方は読んでみてください。
processing.mのパスを相対パスで表現すると次のようになります。
../../Analysis/scripts/processing.m
ディレクトリ構造がもっと複雑な場合には特に、このように相対パスで表したくなると思います。
ですが今欲しいのは絶対パスです。
../../Analysis/scripts/processing.m
を
/Users/ray/Analysis/scripts/processing.m
に変換したいのです!
この目的を果たすためには公式のfileattrib関数が便利なのですが、この関数では存在しないパス(上記例のscriptsフォルダなど)を扱うことができません。
そこで今回はfileattrib関数を元にして、指定したパスが存在しない場合にも対応できる、相対パスを絶対パスに変換する関数(rel2abs.m)を作成しました。
自作関数(rel2abs)
自作関数rel2absのコードを公開します。
このコード(rel2abs.m)はこちらよりダウンロード可能です。
function abspath = rel2abs(relpath) % 簡易的なエラー処理 if ~(ischar(relpath) || iscell(relpath) || isstring(relpath)) warning('引数はchar型,cell型,string型のいずれかにしてください.') abspath = relpath; return end % relpathが要素数1のstring型の場合,char型に変換 num = numel(relpath); outString = 0; if isstring(relpath) && num == 1 relpath = char(relpath); outString = 1; end % relpathが要素数2以上のstring配列の場合,各要素ごとに処理 if isstring(relpath) && num >= 2 abspath = string; for ii = 1:num abspath(ii) = rel2abs(relpath(ii)); end abspath = reshape(abspath, size(relpath)); return end % relpathがcell配列の場合,各要素ごとに処理 if iscell(relpath) abspath = {}; for ii = 1:num abspath{ii} = rel2abs(relpath{ii}); end abspath = reshape(abspath, size(relpath)); return end % ファイル名のみの場合,現在のパスを追加 [pathname, filename, ext] = fileparts(relpath); if isempty(pathname) relpath = fullfile('.', [filename, ext]); end try % 絶対パスに変換 [~, vals] = fileattrib(relpath); abspath = vals.Name; catch % エラーが生じた場合,1段上の階層で同じ処理を試みる % エラーが出るのは主に,引数として指定したパスが存在しない場合 folders = strsplit(relpath, {'/', '¥', '\\'}); % Macのパスに対応 if isempty(folders{1}) folders{2} = ['/', folders{2}]; folders = folders(2:end); end if length(folders) == 1 % 最上階層にきたら処理を終了 abspath = folders{1}; elseif length(folders) >= 2 % 最上階層でなければ1段上の階層へ tmp = folders{1}; for ii = 2:(length(folders) - 1) tmp = fullfile(tmp, folders{ii}); end tmp2 = rel2abs(tmp); % 1段上の階層でrel2abs abspath = fullfile(tmp2, folders{end}); end end % try&catch % 引数がstring型なら戻り値もstring型に変換 if outString abspath = string(abspath); end end % function
この関数を使って、
rel2abs(‘../../Analysis/scripts/processing.m’)
を実行すると、欲しかった絶対パス
/Users/ray/Analysis/scripts/processing.m
を返してくれます。
型は引数に合わせてcharかstringで返します。
解説(rel2abs)
キーポイントとその問題点
rel2absの最重要ポイントは次の2行です。
[~, vals] = fileattrib(relpath); abspath = vals.Name;
fileattrib関数は、引数として渡したファイルやフォルダーの様々な情報を設定したり取得したりする関数です。
2つ目の戻り値valsは構造体となっていて、vals.Nameがファイル名やフォルダー名を表す文字配列になっています。
このfileattrib関数の引数には、相対パスを渡しても問題なく動作するので、今回の目的に非常に有用な関数です!
しかし1点問題があります。
それは、
存在しないファイル名やフォルダー名を引数にするとエラーとなる
という点です。
今回の目的は、まだ存在しないフォルダ内のまだ存在しないファイル名への相対パスを絶対パスに変換することです。
そのためにrel2abs関数では、fileattrib関数のエラーをtry/catch関数によって分岐処理しています。
fileattrib関数がエラーを吐くのは、指定したファイルやフォルダが存在しない場合です。
そこで、「エラーが生じたら1つ上のディレクトリ階層に上がってfileattrib関数を適用すること」を、エラーが出なくなるまで繰り返すことにしました。
階層を上がっていけば存在するパスに到達するだろうという考えの元でこのような処理にしました。
私自身は今のところ問題は生じていないので、それなりに使える関数だと思います。
しかし、おそらく詰めが甘いので、何かご意見がございましたらご教示いただけると助かります!
引数としてCell配列やString配列にも対応
複数の相対パスを一気に絶対パスに変換できるように、
% relpathが要素数1のstring型の場合,char型に変換 num = numel(relpath); outString = 0; if isstring(relpath) && num == 1 relpath = char(relpath); outString = 1; end % relpathが要素数2以上のstring配列の場合,各要素ごとに処理 if isstring(relpath) && num >= 2 abspath = string; for ii = 1:num abspath(ii) = rel2abs(relpath(ii)); end abspath = reshape(abspath, size(relpath)); return end % relpathがcell配列の場合,各要素ごとに処理 if iscell(relpath) abspath = {}; for ii = 1:num abspath{ii} = rel2abs(relpath{ii}); end abspath = reshape(abspath, size(relpath)); return end
とすることで、string配列やcell配列にも簡易的に対応させました。
各要素がchar型またはstring型の文字列であれば、すべて絶対パスに変換して返します。
ファイル名のみを引数として渡した場合
rel2abs関数の引数としてファイル名のみを渡した場合、current directory直下に指定したファイルが存在するような絶対パスを返します。
% ファイル名のみの場合,現在のパスを追加 [pathname, filename, ext] = fileparts(relpath); if isempty(pathname) relpath = fullfile('.', [filename, ext]); end
例えば、current directoryが、
/Users/ray/Experiments/Exp2
のときに、
rel2abs(‘test.mat‘)
を実行すると、
/Users/ray/Experiments/Exp2/test.mat
を返します。
使用例(rel2abs)
例①:1つの相対パスを絶対パスに変換
abspath = rel2abs('../folder1/file1.mat');
Current directoryを「/Users/name/Experiments」とすると、実行結果(戻り値)は、
abspath = ‘/Users/name/folder1/file1.mat’
となります。
この例では引数をchar型としましたが、string型で渡すと戻り値もstring型となります。
また、ファイル名でなくフォルダ名を引数として渡すこともできます。
例②:複数の相対パスを絶対パスに変換
relpath = ... ["../folder1", "../folder1/file1.mat"; "../folder2", "../folder2/file2.mat"]; abspath = rel2abs(relpath);
Current directoryを「/Users/name/Experiments」とすると、実行結果(戻り値)は、
abspath(1,1) = “/Users/name/folder1”
abspath(1,2) = “/Users/name/folder1/file1.mat”
abspath(2,1) = “/Users/name/folder2”
abspath(2,2) = “/Users/name/folder2/file2.mat”
となります。
この例では引数をstring配列としましたが、cell配列で渡すと戻り値もcell配列となります。
まとめ
今回の記事では、MATLABで相対パスを絶対パスに変換する方法を紹介しました。
ある程度需要のある機能だと思うので、同じような問題で困っていた方や、簡単な方法を探していた方はぜひこの記事を参考にしてください。
こちらからダウンロードもできるrel2abs関数は自由にお使いいただいて構いません。
ご意見やご感想などございましたら、TwitterやContactページよりお願いします!
コメント