C# Tips | ファイル・ディレクトリ操作:禁則文字除去

C sharp C#
スポンサーリンク

はじめに 「禁則文字除去」は“事故る前の最後のガード”

業務でファイル名やフォルダ名を「プログラム側で決める」こと、よくありますよね。

ユーザーが入力したタイトルをそのままファイル名にしたい。
日付やIDを組み合わせてログファイル名を作りたい。
外部システムから渡された文字列をフォルダ名にしたい。

ここで何も考えずに文字列をそのまま使うと、こうなります。

「パスに使用できない文字です」と言われて例外で落ちる。
一部の環境では動くのに、別の環境では動かない。
最悪、意図しない場所にファイルを作ってしまう。

これを防ぐためにやるのが「禁則文字除去」です。
つまり、「ファイル名やパスに使ってはいけない文字(禁則文字)を、事前に取り除く/置き換える」という処理です。

ここでは、C# 初心者向けに、

禁則文字とは何か
.NET が提供している「禁則文字一覧」の取り方
実務で使える「禁則文字除去ユーティリティ」の作り方
よくある落とし穴と、どう設計しておくと安全か

を、例題付きでかみ砕いて説明していきます。


禁則文字とは何かをちゃんと押さえる

「見た目は普通の文字列」でも OS 的にはアウトな文字がある

Windows では、ファイル名やパスに使えない文字が決まっています。
代表的なものは次のようなものです。

* ? : " < > | などの記号
制御文字(タブ、改行、その他の不可視文字)

例えば、次のようなファイル名はアウトです。

売上レポート:2025/01/28?.csv
log*2025-01-28.txt

見た目には「まあありそう」な文字列ですが、:/?* が含まれているため、
そのまま File.Create などに渡すと例外になります。

自分で「禁則文字リスト」を決め打ちしない

ここでやりがちなのが、「たぶんこの辺がダメだろう」と自分でリストを作ってしまうことです。

// こういうのはやらないほうがいい
char[] invalid = { '*', '?', ':', '"', '<', '>', '|' };
C#

一見正しそうですが、これだと制御文字などが漏れますし、
OS やランタイムの仕様変更にも追従できません。

C#(.NET)には、ちゃんと「禁則文字を教えてくれるメソッド」が用意されています。
それを使うのが、正しくてラクなやり方です。


.NET が教えてくれる禁則文字 Path.GetInvalidFileNameChars / GetInvalidPathChars

ファイル名用とパス用でメソッドが分かれている

.NET には、次の 2 つのメソッドがあります。

Path.GetInvalidFileNameChars()
ファイル名(report.txt のような「名前+拡張子」部分)に使えない文字の一覧を返す。

Path.GetInvalidPathChars()
パス全体(C:\data\report.txt のようなもの)に使えない文字の一覧を返す。

どちらも戻り値は char[] です。
この配列に含まれている文字が「禁則文字」です。

実際に中身を見てみる

簡単なサンプルで中身を覗いてみましょう。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        Console.WriteLine("ファイル名に使えない文字:");
        foreach (char c in Path.GetInvalidFileNameChars())
        {
            Console.WriteLine($"'{c}' (U+{(int)c:X4})");
        }

        Console.WriteLine();
        Console.WriteLine("パスに使えない文字:");
        foreach (char c in Path.GetInvalidPathChars())
        {
            Console.WriteLine($"'{c}' (U+{(int)c:X4})");
        }
    }
}
C#

実務では中身を毎回見る必要はありませんが、
「OS がこういうリストを持っていて、それを .NET 経由で取れるんだ」という感覚を持っておくと、
「自分で決め打ちしないほうがいい理由」が腑に落ちます。


禁則文字除去ユーティリティを作る(ファイル名編)

基本方針は「禁則文字を安全な文字に置き換える」

禁則文字除去の一番シンプルな方針は、

禁則文字が出てきたら、指定した文字(例: _)に置き換える

です。

削除してしまう手もありますが、
「どこがどう変わったか分かりにくくなる」「同じ名前になりやすい」といったデメリットもあるので、
まずは置き換え方式から入るのがおすすめです。

ファイル名用の禁則文字除去メソッド

using System;
using System.IO;
using System.Linq;

public static class ForbiddenCharRemover
{
    public static string SanitizeFileName(string fileName, char replacement = '_')
    {
        if (fileName is null)
        {
            throw new ArgumentNullException(nameof(fileName));
        }

        char[] invalid = Path.GetInvalidFileNameChars();

        var sanitizedChars = fileName
            .Select(ch => invalid.Contains(ch) ? replacement : ch)
            .ToArray();

        return new string(sanitizedChars);
    }
}
C#

ここでの重要ポイントを丁寧に見ていきます。

Path.GetInvalidFileNameChars() を毎回呼んでいる
禁則文字リストは OS 依存ですが、通常は頻繁に変わりません。
パフォーマンスが気になるなら静的フィールドにキャッシュしてもよいですが、
初心者のうちはまず「正しく動くこと」を優先して OK です。

invalid.Contains(ch) で「禁則文字かどうか」を判定している
禁則文字に該当する場合だけ replacement に置き換え、それ以外はそのまま残します。

replacement を引数にしている
デフォルトは _ にしていますが、呼び出し側で '-'' ' などに変えたい場合もあるので、
引数で差し替えられるようにしておくと柔軟です。

例:ユーザー入力を安全なファイル名に変換する

string userInput = "売上レポート:2025/01/28? *確定版*";

string safeName = ForbiddenCharRemover.SanitizeFileName(userInput) + ".txt";

Console.WriteLine(safeName);
// 例: 売上レポート_2025_01_28__ _確定版_.txt
C#

: / ? * などが _ に置き換えられ、
OS 的に有効なファイル名になります。


禁則文字除去ユーティリティを作る(パス編)

「パス全体」を対象に禁則文字を除去したい場合

ファイル名だけでなく、「ユーザーが入力したパス文字列」をそのまま使いたいケースもあります。
その場合は、Path.GetInvalidPathChars() を使います。

public static class ForbiddenCharRemover
{
    public static string SanitizePath(string path, char replacement = '_')
    {
        if (path is null)
        {
            throw new ArgumentNullException(nameof(path));
        }

        char[] invalid = Path.GetInvalidPathChars();

        var sanitizedChars = path
            .Select(ch => invalid.Contains(ch) ? replacement : ch)
            .ToArray();

        return new string(sanitizedChars);
    }
}
C#

ただし、ここで一つ大事な注意があります。

「パス全体」をサニタイズするときに、
C:\data\file.txt:\ まで置き換えてしまうと、
そもそもパスとして成立しなくなります。

GetInvalidPathChars は、「パスとして絶対にダメな文字」だけを教えてくれるので、
:\ は含まれていません。
つまり、「パスとして意味のある記号」はそのまま残り、「本当にダメな文字だけが置き換えられる」イメージです。

ファイル名部分だけをサニタイズするパターン

実務では、「パスのうち、ファイル名部分だけをサニタイズしたい」ということも多いです。

public static string SanitizeFilePath(string path, char replacement = '_')
{
    if (path is null)
    {
        throw new ArgumentNullException(nameof(path));
    }

    string? directory = Path.GetDirectoryName(path);
    string fileName = Path.GetFileName(path);

    string safeFileName = ForbiddenCharRemover.SanitizeFileName(fileName, replacement);

    if (string.IsNullOrEmpty(directory))
    {
        return safeFileName;
    }

    return Path.Combine(directory, safeFileName);
}
C#

使い方の例です。

string originalPath = @"C:\data\売上レポート:2025/01/28?.csv";

string safePath = ForbiddenCharRemover.SanitizeFilePath(originalPath);

Console.WriteLine(safePath);
// 例: C:\data\売上レポート_2025_01_28_.csv
C#

ここでは、「ディレクトリ部分はそのまま」「ファイル名部分だけ禁則文字除去」という形にしています。
これが一番実務で使いやすいパターンです。


禁則文字除去の“設計ポイント”を押さえる

置き換えるか、削除するか

禁則文字に対して、

置き換える(*_ など)
削除する(* を消してしまう)

どちらも選択肢としてありえます。

置き換えのメリット
元の文字列の「形」がある程度残るので、人間が見たときに意味が分かりやすい。
同じ入力から同じ結果が得られやすい。

削除のメリット
ファイル名が少し短くなる。
禁則文字が多い場合でも _ だらけにならない。

どちらが正解というより、「プロジェクトとしてどちらの方針にするか」を決めて、
ユーティリティの中で統一しておくのが大事です。

削除版の例も載せておきます。

public static string RemoveForbiddenFromFileName(string fileName)
{
    if (fileName is null)
    {
        throw new ArgumentNullException(nameof(fileName));
    }

    char[] invalid = Path.GetInvalidFileNameChars();

    var filteredChars = fileName
        .Where(ch => !invalid.Contains(ch))
        .ToArray();

    return new string(filteredChars);
}
C#

「禁則文字除去だけ」では安全性は足りないこともある

禁則文字除去はあくまで「OS 的に有効な文字列にする」ための処理です。
セキュリティ的には、次のようなことも合わせて考える必要があります。

相対パス(..\..\)で上位ディレクトリに抜けていないか
許可していないルートフォルダの外を指していないか
異常に長いパス・ファイル名になっていないか

これらは「パス安全化」「ルート配下チェック」の話になるので、
禁則文字除去ユーティリティとは別に、もう一段上のレイヤーで扱うのがよいです。


まとめ 「禁則文字除去」は“ファイル名を外部から受け取るなら必須の習慣”

禁則文字除去は、派手さはないけれど、
「ユーザー入力や外部データをそのままファイル名にしない」という、
業務システムではほぼ必須の習慣です。

最後にポイントを整理すると、こうなります。

禁則文字は自分で決め打ちせず、Path.GetInvalidFileNameChars / GetInvalidPathChars から取得する。
基本方針は「禁則文字を安全な文字(例: _)に置き換える」。削除する方針もありだが、プロジェクト内で統一する。
ファイル名だけをサニタイズする場合は、Path.GetFileNamePath.GetDirectoryName を使って分割し、ファイル名部分だけ処理する。
禁則文字除去は「OS 的に有効な文字列にする」ための処理であり、パスの正規化やルート配下チェックと組み合わせると、より安全なユーティリティになる。

ここまでできれば、「とりあえず文字列をそのままファイル名に突っ込む」段階から一歩抜け出して、
業務で長く使える“事故りにくいファイル操作コード”を書けるようになっていきます。

タイトルとURLをコピーしました