はじめに 「ファイル属性変更」ができると何が嬉しいのか
業務でファイルを扱っていると、こんなことが起きます。
「ユーザーがコピーしてきたファイルが読み取り専用で、削除できない」
「ログファイルを隠しファイルにして、普段はユーザーに見せたくない」
「バックアップファイルには読み取り専用を付けて、うっかり上書きされないようにしたい」
こういうときに使うのが「ファイル属性変更」です。
C# では、File.GetAttributes と File.SetAttributes、そして FileAttributes 列挙体を組み合わせることで、「読み取り専用」「隠しファイル」などの属性を自由に付けたり外したりできます。
ここでは、プログラミング初心者向けに、「属性とは何か」から始めて、「読み取り専用を付ける・外す」「隠しファイルにする」「複数属性を組み合わせる」まで、実務で使える形で丁寧に解説していきます。
基本の考え方 FileAttributes と Get/SetAttributes
ファイル属性は「フラグの集合」
Windows のファイルには、「読み取り専用」「隠し」「システム」「アーカイブ」などの属性が付けられます。
C# では、これらを FileAttributes という列挙型で表現します。
代表的なものだけ挙げると、こんな感じです。
FileAttributes.ReadOnly … 読み取り専用FileAttributes.Hidden … 隠しファイルFileAttributes.System … システムファイルFileAttributes.Archive … バックアップ対象などに使われるフラグ
重要なのは、「これらは単独ではなく、組み合わせて使われる」ということです。
例えば、Archive, ReadOnly のように、「アーカイブかつ読み取り専用」という状態になります。
属性を取得する File.GetAttributes
まずは、「今そのファイルにどんな属性が付いているか」を知るところからです。
using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\data\report.csv";
FileAttributes attrs = File.GetAttributes(path);
Console.WriteLine(attrs);
}
}
C#この attrs を表示すると、例えば Archive, ReadOnly のように出てきます。
これは、「複数のフラグが OR された値」になっているからです。
属性を設定する File.SetAttributes
属性を変更するときは、File.SetAttributes を使います。
using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\data\report.csv";
FileAttributes newAttrs = FileAttributes.ReadOnly;
File.SetAttributes(path, newAttrs);
Console.WriteLine("属性を設定しました。");
}
}
C#ここでの注意点は、「SetAttributes は『全部上書き』する」ということです。
今付いている属性に追加するのではなく、「指定した属性だけの状態」にしてしまいます。
だからこそ、「今の属性を取得してから、そこにフラグを足したり引いたりする」という書き方が重要になります。
読み取り専用を付ける・外す
読み取り専用を付ける(追加する)
「今の属性はそのままにして、読み取り専用だけ追加したい」というのが、実務で一番よくあるパターンです。
その場合は、ビット演算の OR(|)を使います。
using System;
using System.IO;
public static class FileAttributeUtil
{
public static void AddReadOnly(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
attrs |= FileAttributes.ReadOnly;
File.SetAttributes(filePath, attrs);
}
}
C#ここでの重要ポイントは attrs |= FileAttributes.ReadOnly; です。
これは、「今の属性に ReadOnly フラグを足す(ON にする)」という意味です。
他の属性(Hidden など)はそのまま残り、ReadOnly だけが追加されます。
読み取り専用を外す(削除する)
逆に、「読み取り専用を外したい(解除したい)」場合は、ビット演算の AND(&)と NOT(~)を組み合わせます。
public static class FileAttributeUtil
{
public static void RemoveReadOnly(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
if ((attrs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
attrs &= ~FileAttributes.ReadOnly;
File.SetAttributes(filePath, attrs);
}
}
}
C#ここで深掘りしたいのが attrs &= ~FileAttributes.ReadOnly; です。
FileAttributes.ReadOnly … ReadOnly ビットだけが 1 の値~FileAttributes.ReadOnly … ReadOnly 以外が 1 のマスクattrs & マスク … ReadOnly ビットだけ 0 にして、他はそのまま
つまり、「ReadOnly だけを OFF にして、他の属性はそのまま残す」という操作になっています。
このパターンは、「特定の属性だけ外したい」ときに何度も出てくるので、体で覚えてしまっていいレベルです。
使い方の例
class Program
{
static void Main()
{
string path = @"C:\data\report.csv";
FileAttributeUtil.AddReadOnly(path);
Console.WriteLine("読み取り専用を付けました。");
FileAttributeUtil.RemoveReadOnly(path);
Console.WriteLine("読み取り専用を外しました。");
}
}
C#これだけで、「読み取り専用の付け外し」を安全に行えるようになります。
隠しファイル・複数属性の組み合わせ
隠しファイルにする(Hidden を付ける)
「ログファイルや一時ファイルを、エクスプローラーで見えにくくしたい」というときは、Hidden 属性を付けます。
やり方は ReadOnly と同じで、OR で追加します。
public static class FileAttributeUtil
{
public static void AddHidden(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
attrs |= FileAttributes.Hidden;
File.SetAttributes(filePath, attrs);
}
public static void RemoveHidden(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
if ((attrs & FileAttributes.Hidden) == FileAttributes.Hidden)
{
attrs &= ~FileAttributes.Hidden;
File.SetAttributes(filePath, attrs);
}
}
}
C#これで、「隠しファイルにする/戻す」を簡単に制御できます。
複数属性を一度に付ける
例えば、「バックアップファイルは読み取り専用かつ隠しファイルにしたい」という場合、次のように書けます。
public static class FileAttributeUtil
{
public static void AddReadOnlyAndHidden(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
attrs |= FileAttributes.ReadOnly;
attrs |= FileAttributes.Hidden;
File.SetAttributes(filePath, attrs);
}
}
C#OR を重ねることで、「今の属性+複数の新しい属性」をまとめて設定できます。
もちろん、attrs |= FileAttributes.ReadOnly | FileAttributes.Hidden; と一行で書いても構いません。
実務で使えるユーティリティ設計
「属性を付ける」「属性を外す」を汎用化する
読み取り専用・隠しファイル以外にも、属性はいくつかあります。
毎回専用メソッドを書くのではなく、「任意の属性を付ける/外す」汎用メソッドを用意しておくと、拡張しやすくなります。
using System;
using System.IO;
public static class FileAttributeUtil
{
public static void AddAttribute(string filePath, FileAttributes attribute)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
attrs |= attribute;
File.SetAttributes(filePath, attrs);
}
public static void RemoveAttribute(string filePath, FileAttributes attribute)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
}
FileAttributes attrs = File.GetAttributes(filePath);
if ((attrs & attribute) == attribute)
{
attrs &= ~attribute;
File.SetAttributes(filePath, attrs);
}
}
}
C#使い方の例は次の通りです。
class Program
{
static void Main()
{
string path = @"C:\data\backup.bak";
FileAttributeUtil.AddAttribute(path, FileAttributes.ReadOnly | FileAttributes.Hidden);
FileAttributeUtil.RemoveAttribute(path, FileAttributes.Hidden);
}
}
C#こうしておくと、「新しい属性を扱いたくなったとき」にも、ユーティリティ側はそのままで済みます。
フォルダ内のファイルに一括で属性を付ける
業務では、「このフォルダ内のファイルを全部読み取り専用にしたい」「特定拡張子だけ隠しファイルにしたい」といった一括操作もよくあります。
using System;
using System.IO;
public static class BulkFileAttributeUtil
{
public static int AddAttributeInDirectory(
string directoryPath,
FileAttributes attribute,
string searchPattern = "*.*")
{
if (!Directory.Exists(directoryPath))
{
throw new DirectoryNotFoundException($"ディレクトリが見つかりません: {directoryPath}");
}
string[] files = Directory.GetFiles(directoryPath, searchPattern);
int count = 0;
foreach (string file in files)
{
FileAttributes attrs = File.GetAttributes(file);
attrs |= attribute;
File.SetAttributes(file, attrs);
count++;
}
return count;
}
}
C#使い方の例は次の通りです。
class Program
{
static void Main()
{
string dir = @"C:\data\backup";
int changed = BulkFileAttributeUtil.AddAttributeInDirectory(
dir,
FileAttributes.ReadOnly,
"*.bak"
);
Console.WriteLine($"属性を変更したファイル数: {changed}");
}
}
C#これで、「バックアップフォルダ内の .bak を全部読み取り専用にする」といった運用が簡単に書けます。
実務での注意点 属性変更は「権限」と「運用ルール」を意識する
属性を変えられないこともある
ファイル属性の変更は、OS の権限に依存します。
例えば、管理者権限が必要なフォルダや、他ユーザーの領域などでは、属性変更が UnauthorizedAccessException で失敗することがあります。
また、ネットワークドライブやクラウドストレージなど、ファイルシステムの種類によっては、属性の扱いがローカルと違うこともあります。
そのため、「属性変更は必ず成功する」とは思わず、例外処理とログ出力をセットで考えるのが大事です。
「勝手に属性を変えていいのか」を必ず考える
読み取り専用を外して削除する、隠し属性を付ける、といった操作は、ユーザーや他システムの期待とズレることがあります。
例えば、「ユーザーが意図的に読み取り専用にしていたファイル」を、システムが勝手に解除してしまうと、トラブルの元になります。
実務では、「どのフォルダ配下はシステム管理」「どこから上はユーザー管理」といった境界を決め、その範囲内だけで属性変更を行う、といった運用ルールを決めておくと安全です。
コードだけでなく、「どこまでやっていいのか」をチームで合意しておくことが、属性変更系ユーティリティではとても重要です。
例外とエラー処理を意識した属性変更
典型的な例外とログのイメージ
属性取得・変更で起こり得る例外は、主に次のようなものです。
ファイルが存在しない(FileNotFoundException)
ディレクトリが存在しない(DirectoryNotFoundException)
権限がない(UnauthorizedAccessException)
入出力エラー(IOException)
呼び出し側での例外処理の例を見てみます。
using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\data\report.csv";
try
{
FileAttributeUtil.AddReadOnly(path);
Console.WriteLine("読み取り専用を付けました。");
}
catch (FileNotFoundException ex)
{
Console.WriteLine("ファイルが見つかりません: " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("権限エラーが発生しました: " + ex.Message);
}
catch (IOException ex)
{
Console.WriteLine("入出力エラーが発生しました: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("想定外のエラーが発生しました: " + ex.Message);
}
}
}
C#実務では、これらのメッセージをログに残しておくことで、「どのファイルの属性変更に、どんな理由で失敗したか」を後から追跡できます。
まとめ 実務で使える「ファイル属性変更」ユーティリティの考え方
ファイル属性変更は、「読み取り専用で消せない」「隠しファイルにしたい」「バックアップを保護したい」といった、現場でよくある悩みを解決するための強力な手段です。
だからこそ、「属性を直接いじる生コード」をバラバラに書くのではなく、ユーティリティとして整理しておく価値があります。
File.GetAttributes と File.SetAttributes を使い、今の属性を取得してからフラグを足したり引いたりすること。attrs |= 属性 で「付ける」、attrs &= ~属性 で「外す」というビット演算パターンを体で覚えること。
読み取り専用・隠しファイルなど、よく使う属性については専用メソッドを用意しつつ、汎用的な AddAttribute / RemoveAttribute も用意しておくこと。
フォルダ単位・再帰的な一括変更ユーティリティを作り、バックアップやログの運用ルールをコードで表現できるようにすること。
権限や運用ルールを意識し、「どこまで属性を変えてよいのか」をチームで決めたうえで使うこと。
ここまで押さえておけば、「属性のせいで書けない・消せない」「どこで属性が変わったのか分からない」といった、現場あるあるのトラブルをかなり減らせます。
