8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Deno】パスやURLの操作方法まとめ2022

Last updated at Posted at 2022-03-22

Denoでパスの操作をする方法について解説します。

基本は URL API を使う

パス文字列を手動で操作する前に、まずはURL APIの使用を検討しましょう。

const pathToFoo = new URL("./foo.txt", import.meta.url);

URL APIはWeb APIの一つです。
URL APIを使うと

  • Windowsにおけるドライブ文字の処理
  • Windows/linuxにおけるパス区切り文字の差異の吸収(スラッシュとバックスラッシュ)
  • パス同士の結合

を全てやってくれます。
特にドライブ文字の処理は手で書いてデバッグするの大変ですから、積極的にURL APIを使いましょう。

パスの結合

パスの結合にはURLコンストラクタを使用します。

const pathA = "./foo.txt";
const pathB = "/path/to/dir/";
const path = new URL(pathA, pathB); // pathBにpathAを結合したもの

URLコンストラクタの左と右に文字列を渡すと両者が結合されたURLが返ります。

自ファイル基準の相対パス

import.meta.urlimport.meta.resolveを使うと、自分のファイルを基準にした相対パスが取れます。

// 自分のファイルが`file:///a/b/c.ts`のとき、`file:///a/b/foo.txt`
// 自分のファイルが`https://example.com/a/b/c.ts`のとき、`https://example.com/a/b/foo.txt`
const relativePath1 = new URL("./foo.txt", import.meta.url);
const relativePath2 = new URL(import.meta.resolve("./foo.txt"));
// 上の2つは同じ値になる

ライブラリ等を https://deno.land/x に公開する場合、import.meta.urlfetchを使うと、ライブラリで読み込まれた時とローカルで実行した時の両方で動作するコードになります。

// このファイルをローカルで実行した場合、`file:///...`
// このファイルが https://deno.land/x からimportされた場合、`https://deno.land/x/...`
const url = new URL("./foo.wasm", import.meta.url);

// fetchは`https://...`と`file:///...`の両方のURLを読むことができる。
// ローカルで実行した場合はローカルファイルが、
// deno.land/x からimportされた場合は deno.land/x からファイルが読み込まれる。
const res = await fetch(url);
console.log(await res.text());

この挙動はwasmファイルの読み込みなどに便利です。

ここで例えばimport.meta.urlの代わりにカレントディレクトリを使ったり、fetchの代わりにDeno.readTextFileを使ったりしてしまうと動きません。

他のAPIと組み合わせて使う

Denoのファイル操作系APIや、fetchなどのWeb APIは、URLオブジェクトを引数に取ることができます。

Deno.readTextFile(new URL(...));
fetch(new URL(...));

標準ライブラリのpathモジュール

URL APIではできない特殊な操作については、標準ライブラリのpathモジュールを使います。

例えばDeno.cwd()で得られるカレントディレクトリはパス文字列ですから、URLとして扱うことはできません。そのため、下のtoFileUrlなどを使ってURLに変換することになります。

fileURLとパス文字列の相互変換

まずはパス文字列とfileURLの変換方法です。標準ライブラリのfromFileUrl/toFileUrlを使います。

import { fromFileUrl, toFileUrl } from "https://deno.land/std@0.130.0/path/mod.ts";

// パス文字列→fileURLの変換
console.log(toFileUrl("/foo/bar.txt")); // => URL { href: "file:///foo/bar.txt" }
console.log(toFileUrl(`${Deno.cwd()}/`)) // => URL { href: "file:///C:/Users/path/to/cwd/"}

// fileURL→パス文字列の変換
console.log(fromFileUrl("file:///foo/bar.txt")); // => "\foo\bar.txt"
console.log(fromFileUrl(new URL("file:///foo/bar.txt"))); // => "\foo\bar.txt"

パス文字列からファイル名・ディレクトリ・拡張子の取得

それぞれbasename/dirname/extnameを使います。

import { basename, dirname, extname } from "https://deno.land/std@0.130.0/path/mod.ts";

console.log(basename("/foo/bar.txt")); // => "bar.txt"
console.log(dirname("/foo/bar.txt")); // => "/foo"
console.log(extname("/foo/bar.txt")); // => ".txt"

パスの結合

パス文字列の結合にはjoinresolveを使います。

import { join, resolve } from "https://deno.land/std@0.130.0/path/mod.ts";

console.log(join("./foo/", "./bar/", "../baz.txt")); // => "foo\baz.txt"
console.log(resolve("./foo/", "./bar/", "../baz.txt")); // => "C:\Users\azusa\work\deno\eki\foo\baz.txt"

joinが単純に複数のパス文字列を結合するのに対し、resolveはカレントディレクトリを考慮した上でフルパスを計算してくれます。

おそらく普通はjoinを使えばいいと思います。

2つのパスの比較

パスAからパスBへの相対パスを計算するrelativeと、パス同士の共通部分を計算するcommonがあります。

import { common, relative } from "https://deno.land/std@0.130.0/path/mod.ts";

// "./foo/"から"./bar/"への相対パスを計算する
console.log(relative("./foo/", "./bar/")); // => "..\\bar"

// ".\\foo\\bar\\a.txt"と".\\foo\\bar\\b.txt"の共通部分を計算する
console.log(common([".\\foo\\bar\\a.txt", ".\\foo\\bar\\b.txt"])); // => ".\\foo\\bar\\"

パスの正規化

normalizeを使います。

import { normalize } from "https://deno.land/std@0.130.0/path/mod.ts";

console.log(normalize("./foo/bar/baz/../../")); // => "foo\\"

globを正規表現に変換

globToRegExpを使います。
詳しい仕様は公式ドキュメント

import { globToRegExp } from "https://deno.land/std@0.130.0/path/mod.ts";

const re = globToRegExp("/foo/*.txt");

console.log(re); // => /^(?:\\|\/)+foo(?:\\|\/)+[^\\/]*\.txt(?:\\|\/)*$/
console.log(re.test("/foo/bar.txt")); // => true
console.log(re.test("/bar/")); // => false

パス文字列のパース

parse関数は、パス文字列をオブジェクトへ変換します。
format関数は、オブジェクトをパス文字列に変換します。

import { format, parse } from "https://deno.land/std@0.130.0/path/mod.ts";

// パス文字列→オブジェクト
const parsed = parse("\\foo\\bar\\baz.txt");
console.log(parsed); // => { root: "\\", dir: "\\foo\\bar", base: "baz.txt", ext: ".txt", name: "baz" }

// オブジェクト→パス文字列
console.log(format(parsed)); // => "\\foo\\bar\\baz.txt"

parse関数の返り値は以下のようなinterfaceを持つオブジェクトです。

{
  /** パスのルート('/' 又は 'c:\') */
  root: string;
  /** ディレクトリのフルパス */
  dir: string;
  /** ファイル名(拡張子付き) */
  base: string;
  /** 拡張子 */
  ext: string;
  /** ファイル名(拡張子なし) */
  name: string;
}

絶対パスかどうかの判定

isAbsoluteを使うと絶対パスかどうか判定できます。

import { isAbsolute } from "https://deno.land/std@0.130.0/path/mod.ts";

console.log(isAbsolute("/foo")); // => true
console.log(isAbsolute("c:/foo")); // => true
console.log(isAbsolute("cat:/foo")); // => false
console.log(isAbsolute("./foo")); // => false

パス区切り文字

sepSEPはパスの区切り文字です。
SEP_PATTERNはパスの区切り文字にマッチする正規表現です。
delimiterは環境変数PATHの区切り文字と思われます。

import { SEP, sep, SEP_PATTERN, delimiter } from "https://deno.land/std@0.130.0/path/mod.ts";

console.log(sep); // => "\\" (windowsとposixで異なる)
console.log(SEP); // => "\\" (windowsとposixで異なる)
console.log(SEP_PATTERN); // => /[\\/]+/
console.log(delimiter) // => ";" (windowsとposixで異なる)

URLのパターンマッチ

URLのパターンマッチにはURLPattern APIを使うことができます。

const pattern = new URLPattern({ pathname: "/foo/:id.txt" });

console.log(pattern.test({ pathname: "/foo/bar.txt" })); // => true
console.log(pattern.exec({ pathname: "/foo/bar.txt" })); // => { pathname: { input: "/foo/bar.txt", groups: { id: "bar" } }, ...}

pattern.test()でマッチしたかどうか(trueかfalse)、pattern.exec()でマッチ結果を得ることができます。詳しい使用方はMDNを見てください。

まとめ

  • パス文字列を直接操作することは避け、極力URL APIを使うのがオススメ
  • 特殊なパス文字列の操作には標準ライブラリのpathモジュールを使う
  • URLへのパターンマッチにはURLPattern APIを使う
8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?