HTMLなどで、リンクは自身のパスからの相対パスで記述することが多いです。
たとえばEPUBリーダーを作っているなら、「OPS/xhtml/0001.xhtml」に含まれる画像やリンクは
<img src="../image/cover.jpg"> → OPS/image/cover.jpg <a href="./0002.xhtml"> → OPS/xhtml/0002.xhtml
のように解釈する必要があります。
もちろん、EPUBファイルはファイルシステムに展開せず、ZIPのまま扱いたいですね。
こんな処理を自前で書いても良いことはないので、なるべく標準ライブラリに近いものを使わせて頂きましょう。
Ruby
真っ先に思いつくのはFile.expand_pathですが、これはあくまで実ファイル用です。「0002.xhtml」という文字列を渡したら、カレントディレクトリからの相対パスとして解釈されてしまい、ベースファイルパスを指定できません。
URI.joinは近いことができますが、scheme指定が必要なので使いづらいです。
この場合、Pathname.cleanpathを使いましょう。
require 'pathname' base = Pathname.new('a/b/c/../d/./../e/f.txt') base.cleanpath.to_s => "a/b/e/f.txt" Pathname.new(base.parent + "../g.html").cleanpath.to_s => "a/b/g.html" Pathname.new(base.parent + "/root.html").cleanpath.to_s => "/root.html"
シンプルで使いやすいですね。
ちなみに、Pathname.realpathは、cleanpathの動作に加えてシンボリックリンクの解決や実ファイルの存在チェックをします。
Java
AndroidでJavaを使って処理する場合はURIUtilsが便利です。
public static String resolvePath(String base, String reference) throws URISyntaxException { URI context = new URI(base); return URIUtils.resolve(context, reference).toString(); }
// 挙動はJUnitで言うとこうなります assertEquals("/", StringUtil.resolvePath("/", "")); assertEquals("index.html", StringUtil.resolvePath("", "index.html")); assertEquals("index.html", StringUtil.resolvePath("", "./index.html")); assertEquals("index.html", StringUtil.resolvePath("", "../index.html")); assertEquals("index.html", StringUtil.resolvePath("hoge.xml", "index.html")); assertEquals("index.html", StringUtil.resolvePath("abc/def/hoge.xml", "../../index.html")); assertEquals("/index.html", StringUtil.resolvePath("/", "index.html")); assertEquals("/index.html", StringUtil.resolvePath("/hoge.xml", "index.html")); assertEquals("/index.html", StringUtil.resolvePath("/abc/def/hoge.xml", "/index.html")); assertEquals("abc/def/index.html", StringUtil.resolvePath("", "abc/def/index.html")); assertEquals("abc/def/index.html", StringUtil.resolvePath("abc/def/ghi/hoge.xml", "../index.html")); assertEquals("abc/def/index.html", StringUtil.resolvePath("abc/def/", "index.html")); // ちょっと注意 assertEquals("index.html", StringUtil.resolvePath("/", "../index.html")); assertEquals("index.html", StringUtil.resolvePath("/hoge.xml", "../index.html"));
C
libxml2のxmlBuildURIが便利です。
#include <stdio.h> #include <libxml/uri.h> int main() { xmlChar *base = (xmlChar *)"abc/def/index.xml"; printf("%s\n", xmlBuildURI((xmlChar *)("../hoge.html"), base)); }