C# Tips | ファイル・ディレクトリ操作:ファイル属性変更

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

はじめに 「ファイル属性変更」ができると何が嬉しいのか

業務でファイルを扱っていると、こんなことが起きます。

「ユーザーがコピーしてきたファイルが読み取り専用で、削除できない」
「ログファイルを隠しファイルにして、普段はユーザーに見せたくない」
「バックアップファイルには読み取り専用を付けて、うっかり上書きされないようにしたい」

こういうときに使うのが「ファイル属性変更」です。
C# では、File.GetAttributesFile.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.GetAttributesFile.SetAttributes を使い、今の属性を取得してからフラグを足したり引いたりすること。
attrs |= 属性 で「付ける」、attrs &= ~属性 で「外す」というビット演算パターンを体で覚えること。
読み取り専用・隠しファイルなど、よく使う属性については専用メソッドを用意しつつ、汎用的な AddAttribute / RemoveAttribute も用意しておくこと。
フォルダ単位・再帰的な一括変更ユーティリティを作り、バックアップやログの運用ルールをコードで表現できるようにすること。
権限や運用ルールを意識し、「どこまで属性を変えてよいのか」をチームで決めたうえで使うこと。

ここまで押さえておけば、「属性のせいで書けない・消せない」「どこで属性が変わったのか分からない」といった、現場あるあるのトラブルをかなり減らせます。

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