「お題:フルパスから相対パスを求める」をjsでやってみた男

説明


二つのフルパスを受け取り、一つ目のパスから二つ目のパスへの相対パスを返す関数を実装せよ。

条件)
・パス区切り文字は / のみサポートする。
・結果パスは カレントディレクトリを表す ./ もしくは、一つ親のディレクトリを表す ../ から始める。
・パスが / で終わる場合ディレクトリとする。
・パスが / で終わらない場合ファイルとする。
・空のパス(空文字列、null、nilなど)が渡された場合、エラーとする。
・パス区切り文字以外でファイル名として使用不可能な文字(参照:http://goo.gl/R0LqY)が含まれている場合、エラーとする。

例1) 同じディレクトリにあるファイル
パス1:/aaa/bbb/from.txt
パス2:/aaa/bbb/to.txt
結果 :./to.txt

例2) 親ディレクトリにあるファイル
パス1:/aaa/bbb/from.txt
パス2:/aaa/to.txt
結果 :../to.txt

例3) 子ディレクトリにあるファイル
パス1:/aaa/bbb/from.txt
パス2:/aaa/bbb/ccc/to.txt
結果 :./ccc/to.txt

例4) 親の子の子にあるファイル
パス1:/aaa/bbb/from.txt
パス2:/aaa/ccc/ddd/to.txt
結果 :../ccc/ddd/to.txt

例5) ルート越え
パス1:/aaa/bbb/from.txt
パス2:/ddd/ccc/to.txt
結果 :../../ddd/ccc/to.txt

例6) ディレクトリからファイル
パス1:/aaa/bbb/
パス2:/aaa/ddd/to
結果 :../ddd/to

例7) ファイルからディレクト
パス1:/aaa/bbb/from
パス2:/aaa/ccc/
結果 :../ccc/

例8) ディレクトリからディレクト
パス1:/aaa/bbb/
パス2:/aaa/ccc/
結果 :../ccc/

例9) 同じパス
パス1:/aaa/bbb/ccc.txt
パス2:/aaa/bbb/ccc.txt
結果 :./ccc.txt

例10) 空
パス1:空
パス2:/bbb/to.txt
結果 :エラー

例11) 使用不可能な文字
パス1:/aaa/g*
パス2:/bbb/to.txt
結果 :エラー

jsでやる

前回同様どううまくやるかという話ですが、やはり思いつきませんでした。
for万歳
尚、どうすればいいかよくわからなかった点は以下のように勝手に決めました。

  • スラッシュが連続したらエラー
  • 先頭にスラッシュが無かったらエラー
function relativePath(from, to) {
  if ([from, to].some(RegExp.prototype.test, /^[^\/]|\.$|^$|[\\\?\*\:\|"<>]|\/\//))
    return "エラー";
  var f = from.substr(1).split("/"), t = to.substr(1).split("/");
  for (var i = 0, l = Math.min(f.length, t.length) - 1; i < l && f[i] === t[i]; ++i);
  return ((new Array(f.length - i)).join("../") || "./") + t.slice(i).join("/");
}

//test
alert(
[
  ["/aaa/bbb/from.txt", "/aaa/bbb/to.txt", "./to.txt"],
  ["/aaa/bbb/from.txt", "/aaa/to.txt", "../to.txt"],
  ["/aaa/bbb/from.txt", "/aaa/bbb/ccc/to.txt", "./ccc/to.txt"],
  ["/aaa/bbb/from.txt", "/aaa/ccc/ddd/to.txt", "../ccc/ddd/to.txt"],
  ["/aaa/bbb/from.txt", "/ddd/ccc/to.txt", "../../ddd/ccc/to.txt"],
  ["/aaa/bbb/", "/aaa/ddd/to", "../ddd/to"],
  ["/aaa/bbb/from", "/aaa/ccc/", "../ccc/"],
  ["/aaa/bbb/", "/aaa/ccc/", "../ccc/"],
  ["/aaa/bbb/ccc.txt", "/aaa/bbb/ccc.txt", "./ccc.txt"],
  ["", "/bbb/to.txt", "エラー"],
  ["/aaa/g*", "/bbb/to.txt", "エラー"]
].every(function (a) {
  return relativePath(a[0], a[1]) === a[2];
})
);