ファイルが存在するか調べるには
ファイルの有無を調べる基本形を各言語で示す。
ディレクトリと区別する判定と、確認してから開くまでの間に状態が変わりうる点まで扱う。
公開:
各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。
Python 実行確認済み
from pathlib import Path
p = Path("a.txt")
if p.is_file():
print("exists")Path.is_file() はパスが存在し、かつ通常のファイルであるときだけ True を返す。
exists() はディレクトリでも True になるため、開くことが前提の判定には is_file() を使う。
JavaScript 実行確認済み
import { stat } from "node:fs/promises";
const exists = await stat("a.txt")
.then((s) => s.isFile())
.catch(() => false);fs/promises の stat は対象が無いと ENOENT で reject するので、catch で false に倒して真偽値にする。
同期版の existsSync は手軽だがイベントループを止めるため、非同期処理の中では避ける。
TypeScript 実行確認済み
import { stat } from "node:fs/promises";
const exists: boolean = await stat("a.txt")
.then((s) => s.isFile())
.catch(() => false);書き方は JavaScript と同じで、catch を挟むと戻り値の型が広がるため boolean と明示しておくと後続の分岐で扱いやすい。
Go 静的確認
info, err := os.Stat("a.txt")
switch {
case errors.Is(err, fs.ErrNotExist):
fmt.Println("not found")
case err != nil:
log.Fatal(err)
case info.Mode().IsRegular():
fmt.Println("exists")
}os.Stat のエラーには権限不足なども含まれるため、err != nil をそのまま「存在しない」と読み替えてはいけない。
存在しないことだけを判定するときは errors.Is(err, fs.ErrNotExist) を使う。
Rust 静的確認
use std::path::Path;
let exists = Path::new("a.txt").is_file();Path::is_file は内部でメタデータを取得し、取得できなければ false を返す。
存在しないのか権限が無いのかを区別したい場合は fs::metadata の Result を直接見る。
Java 静的確認
import java.nio.file.*;
boolean exists = Files.isRegularFile(Path.of("a.txt"));Files.isRegularFile は存在しない場合も属性を読めない場合も false を返す。
Files.exists はディレクトリでも true になるため、ファイルとして開く前提なら isRegularFile を選ぶ。
C# 静的確認
using System.IO;
bool exists = File.Exists("a.txt");File.Exists は対象がディレクトリのときや権限が無いときも例外を投げず false を返す。
判定できなかったのか本当に無いのかを区別したい場合は、FileInfo や実際の open の例外で確かめる。
存在確認とディレクトリの区別
多くの言語には「存在する」を返す関数と「通常ファイルである」を返す関数の両方がある。
前者は同名のディレクトリがあっても真を返すため、これから開いて読むつもりのパスに対しては後者を使うのが安全である。
ディレクトリを開こうとして実行時に失敗する事故は、この取り違えから起きる。
つまずき
存在確認をしてから実際に開くまでの間に、別のプロセスがそのファイルを消したり作ったりできる。
したがって「確認してから開く」は競合状態を完全には防げない。
確実に扱いたい場合は、確認せずに開いてしまい、失敗したときの例外やエラーを捕まえるほうがよい。
存在確認は「無ければ処理をスキップする」といった、ずれても実害の小さい用途に向く。