はじめに 「禁則文字除去」は“事故る前の最後のガード”
業務でファイル名やフォルダ名を「プログラム側で決める」こと、よくありますよね。
ユーザーが入力したタイトルをそのままファイル名にしたい。
日付やIDを組み合わせてログファイル名を作りたい。
外部システムから渡された文字列をフォルダ名にしたい。
ここで何も考えずに文字列をそのまま使うと、こうなります。
「パスに使用できない文字です」と言われて例外で落ちる。
一部の環境では動くのに、別の環境では動かない。
最悪、意図しない場所にファイルを作ってしまう。
これを防ぐためにやるのが「禁則文字除去」です。
つまり、「ファイル名やパスに使ってはいけない文字(禁則文字)を、事前に取り除く/置き換える」という処理です。
ここでは、C# 初心者向けに、
禁則文字とは何か
.NET が提供している「禁則文字一覧」の取り方
実務で使える「禁則文字除去ユーティリティ」の作り方
よくある落とし穴と、どう設計しておくと安全か
を、例題付きでかみ砕いて説明していきます。
禁則文字とは何かをちゃんと押さえる
「見た目は普通の文字列」でも OS 的にはアウトな文字がある
Windows では、ファイル名やパスに使えない文字が決まっています。
代表的なものは次のようなものです。
* ? : " < > | などの記号
制御文字(タブ、改行、その他の不可視文字)
例えば、次のようなファイル名はアウトです。
売上レポート:2025/01/28?.csvlog*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.GetFileName と Path.GetDirectoryName を使って分割し、ファイル名部分だけ処理する。
禁則文字除去は「OS 的に有効な文字列にする」ための処理であり、パスの正規化やルート配下チェックと組み合わせると、より安全なユーティリティになる。
ここまでできれば、「とりあえず文字列をそのままファイル名に突っ込む」段階から一歩抜け出して、
業務で長く使える“事故りにくいファイル操作コード”を書けるようになっていきます。
