std:path — パス文字列操作
標準ライブラリモジュール (index.md)。本書中の裸の §N は本書の節を指す。言語の意味論は ../language-spec.md を参照。
import path := "std:path" は join / dir / base / ext / stem / normalize / is_absolute / split の 8 slot を持つ namespace object を path に束縛する。パス文字列の字句操作 (連結・分解・正規化) を担う。
std:fs / std:term と違い I/O を伴わず、純粋・同期 (Future を返さない)。すべて String 上の純粋関数で、実際のファイルアクセスは呼び出し側が std:fs 等で行う。パスの存在確認やディレクトリ判定はせず、与えられた文字列だけを見て答える (字句的)。
import { join, dir, base, ext } := "std:path" join(["data", "2026-06-19", "note.md"]) #> "data/2026-06-19/note.md" dir("data/2026-06-19/note.md") #> "data/2026-06-19" base("data/2026-06-19/note.md") #> "note.md" ext("note.md") #> ".md"
| 名前 | 形 | 意味 |
|---|---|---|
join |
join(parts) |
parts (String の List) をパス区切りで連結し、正規化した String を返す |
dir |
dir(path) |
path から末尾要素を除いた親ディレクトリ (正規化済み) の String を返す |
base |
base(path) |
path の末尾要素 (basename) の String を返す |
ext |
ext(path) |
path の末尾要素の拡張子 (先頭ドット込み) の String を返す。無ければ "" |
stem |
stem(path) |
base(path) から ext(path) を除いた部分の String を返す |
normalize |
normalize(path) |
path の冗長なパス区切り・. / .. をまとめた String を返す |
is_absolute |
is_absolute(path) |
path が絶対パスなら true、相対なら false (Bool) を返す |
split |
split(path) |
{dir, base} の closed・immutable なレコードを返す |
本モジュールは効果を1 つも持たない(../language-spec.md §17.9)。
以下の例では import path := "std:path" で束縛したものとして path.join 等で記す。全関数は値を 直接返す (Future でも Result でもない)。非 String 引数・join への非 List / 非 String 要素は回復可能エラーではなく、呼び出し位置で Error (panic 型, language-spec.md §16) になる (§9)。
1. パス区切りと正規化
パス区切りとは 1 本のパスの中で要素を分ける記号をいう。PATH 環境変数のように複数のパスを 1 つの文字列へ並べるときの境目 (デリミター) とは別のものである。
- パス区切りは OS native — POSIX 系では
/、Windows では\。どちらになるかは処理系が動いているホストで決まり、実行時に選べる口は無い。std:fsがパスの解釈をホストへ委ねるのと同じ向きである (fs.md §12) - 「正規化」とは冗長なパス区切りの畳み込み (
a//b→a/b)、.の除去、..の親方向への適用、末尾パス区切りの除去をいう。..は根より上へは出ない — 根を持つパスでは根で止まり (/../aは/a)、根を持たないパスでは..がそのまま残る (a/../..は..) - すべて 字句的 に行う。FS にアクセスしないので、シンボリックリンクの解決や存在確認はしない
- 入力 String は OS native なパス区切りで書かれているものとみなす。パス区切りを含まない文字列は単一要素のパスとして扱う
1.1 Windows のパス区切りとボリューム名
Windows ホストでは次の 2 点が POSIX 系と違う。以降の各節の例は POSIX 系のホストのものである。
- 入力は
\と/の両方をパス区切りとして受け、出力は\に揃える - パスの先頭に ボリューム名が付きうる。形は 2 つで、ドライブ (
C:— ラテン 1 文字と:。大小どちらも受ける) と UNC 共有 (\\server\share)。正規化はボリューム名をそのまま保ち (大小もまとめない)、..はボリューム名より上へ出ない
ボリューム名は末尾要素の取り出しにも効く。base はボリューム名を末尾要素とみなさないので、C:\ も C: も末尾要素はパス区切り 1 文字 (\) である。絶対かどうかもボリューム名で決まる (§8)。
次の表は Windows ホストでの答えである。綴りは実際のパスで書いてある — Hikari のソースに書くときは \ を \\ とエスケープする (language-spec.md §7.2)。
| 綴り | normalize |
dir |
base |
is_absolute |
|---|---|---|---|---|
C:\a\b |
C:\a\b |
C:\a |
b |
true |
C:/a/b |
C:\a\b |
C:\a |
b |
true |
C:\a\..\b |
C:\b |
C:\ |
b |
true |
C:\..\a |
C:\a |
C:\ |
a |
true |
C:\ |
C:\ |
C:\ |
\ |
true |
C: |
C:. |
C:. |
\ |
false |
C:a\b |
C:a\b |
C:a |
b |
false |
C:a\..\.. |
C:.. |
C:. |
.. |
false |
\\server\share\a |
\\server\share\a |
\\server\share\ |
a |
true |
\\server\share |
\\server\share |
\\server\share |
\ |
true |
\a\b |
\a\b |
\a |
b |
false |
a\b |
a\b |
a |
b |
false |
C:\..\a が C:\a になるのは §1 の「.. は根より上へは出ない」がボリューム名にも効くからで、根を持たない C:a\..\.. では POSIX 系と同じく .. が残る。
2. path.join
- 引数:
parts(String の List) 1 個。非 List、または List 要素に非 String を含むと呼び出し位置でError(§10) partsの各要素をパス区切りで連結し、結果を 正規化 した String を返す- 空文字列の要素は無視する。すべて空または
partsが空 List[]のときは""を返す
path.join(["a", "b", "c"]) #> "a/b/c" path.join(["a/", "/b"]) #> "a/b" (冗長なパス区切りをまとめる) path.join(["a", "", "b"]) #> "a/b" (空要素を無視) path.join(["a", "..", "b"]) #> "b" (.. を字句的に適用) path.join([]) #> ""
3. path.dir
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathから末尾要素を除いた親ディレクトリを 正規化して 返す- 末尾のパス区切りは無視して末尾要素を決める
path.dir("a/b/c") #> "a/b" path.dir("a/b/") #> "a" path.dir("a") #> "." (パス区切りを含まない → カレント) path.dir("/a") #> "/" path.dir("/") #> "/" path.dir("") #> "."
4. path.base
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathの末尾要素 (basename) を返す。末尾のパス区切りは除いてから末尾要素を取る
path.base("a/b/c") #> "c" path.base("a/b/") #> "b" path.base("a") #> "a" path.base("/") #> "/" path.base("") #> "."
5. path.ext
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathの 末尾要素 の最後の.以降を、先頭ドット込み で返す。.が無ければ""- 拡張子の判定は末尾要素 (basename) に対してのみ行う。ディレクトリ部分の
.は見ない - 末尾要素は §4 と同じ決め方をする。末尾のパス区切りは除いてから採る
path.ext("note.md") #> ".md" path.ext("a.tar.gz") #> ".gz" (最後のドット以降) path.ext("note") #> "" path.ext("a.b/c") #> "" (拡張子判定は末尾要素 "c" のみ) path.ext(".gitignore") #> ".gitignore" (末尾要素の最初の文字が "." でもその位置のドットを採る) path.ext("a/b.txt/") #> ".txt" (末尾のパス区切りは末尾要素の決め方を変えない) path.ext("") #> "." (空の綴りの末尾要素は base と同じく "." である)
6. path.stem
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) base(path)から末尾のext(path)を取り除いた部分を返す。定義上stem(p) ++ ext(p) == base(p)(++は文字列連結を表す説明用記法)
path.stem("a/note.md") #> "note" path.stem("a.tar.gz") #> "a.tar" path.stem("note") #> "note" path.stem(".gitignore") #> "" (base == ext なので stem は空)
7. path.normalize
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathを §1 の規則で正規化した String を返す。FS にアクセスせず字句的にまとめるだけ- 空文字列は
"."に正規化する
path.normalize("a//b/./c") #> "a/b/c" path.normalize("a/b/../c") #> "a/c" path.normalize("./a") #> "a" path.normalize("") #> "." path.normalize("a/b/") #> "a/b" (末尾のパス区切りを除く)
8. path.is_absolute
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathが絶対パスならtrue、相対ならfalseを返す (Bool)。字句的判定 で、FS にアクセスしない- 判定基準は OS native。POSIX 系では先頭がパス区切りであること。Windows ではドライブ付きで根を持つ (
C:\a) か UNC 共有で始まる (\\server\share\a) ことで、ドライブ相対のC:aとボリューム名を持たない\aは絶対ではない (§1.1)
path.is_absolute("/a/b") #> true (POSIX 系) path.is_absolute("a/b") #> false path.is_absolute("./a") #> false path.is_absolute("") #> false
9. path.split
- 引数:
path(String) 1 個。非 String は呼び出し位置でError(§10) pathを親ディレクトリと末尾要素に分け、スロット{dir, base}を持つ closed・immutable なレコード を返す:- 定義上
split(p).dir == dir(p)かつsplit(p).base == base(p)。dirとbaseを 1 回の呼び出しでまとめて得る糖衣
match path.split("a/b/c") { r: Any => print(r.dir); print(r.base) # "a/b" と "c" } s := path.split("note.md") s.dir #> "." s.base #> "note.md"
10. エラーモデル
std:path の全関数は 値を直接返し、回復可能エラー (Result / {kind, message}) を返さない。パスの字句操作は入力が String (および join では String の List) でありさえすれば失敗しないためである。
型が合わない呼び出しは回復可能エラーではなく、呼び出し位置で Error (panic 型, language-spec.md §16) になる:
- いずれかの関数に 非 String の
pathを渡す joinに 非 List を渡す、または List 要素に 非 String を含む
これは json.md §5 の引数型エラー (非 String の text 等) と同じ規律で、std:fs の I/O 失敗 ({kind, message} の Err) とは別の層である。