C#でファイル名を変更するにはFile.Move(元ファイルパス,後ファイルパス)
を使うのが一番ラクですが、実はこのメソッドには2つ落とし穴があるので、それを回避する方法を説明します。
フォルダ(ディレクトリ)では使えない
File
なので当たり前といえばそうですが、フォルダかどうかは気にせずファイル名を変更したいですね。
フォルダの場合は代わりにDirectory.Move()
を使用します。
そして、指定されたパスがフォルダかどうかはFileInfo.Attributes
から調べます。
public static void Rename(string sourceFilePath, string outputFilePath)
{
var fileInfo = new FileInfo(sourceFilePath);
if (fileInfo.Attributes.HasFlag(FileAttributes.Directory))
{
Directory.Move(fileInfo.FullName, outputFilePath);
}
else
{
fileInfo.MoveTo(outputFilePath);
}
}
フォルダ(ディレクトリ)は大文字小文字だけの変更はできない
たとえば、C:\abc
→C:\ABC
といった変更は直接できません。ですので一度別のファイル名に変更してから再変更します。
public static void RenameDirectory(string sourceFilePath, string outputFilePath)
{
if ((String.Compare(sourceFilePath, outputFilePath, true) == 0))
{
var tempPath = GetSafeTempName(outputFilePath);
Directory.Move(sourceFilePath, tempPath);
Directory.Move(tempPath, outputFilePath);
}
else
{
Directory.Move(sourceFilePath, outputFilePath);
}
}
private static string GetSafeTempName(string outputFilePath)
{
outputFilePath += "_";
while (File.Exists(outputFilePath))
{
outputFilePath += "_";
}
return outputFilePath;
}
なお、この問題はFile.Move()
では発生しません。謎い。
まとめ
以上2つの解決策をまとめると、以下のようになります。
他にもこんな落とし穴がある、とかあったら教えてください。
/// <summary>
/// 確実にファイル/ディレクトリの名前を変更する
/// </summary>
/// <param name="sourceFilePath">変更元ファイルパス</param>
/// <param name="outputFilePath">変更後ファイルパス</param>
public static void Rename(string sourceFilePath, string outputFilePath)
{
var fileInfo = new FileInfo(sourceFilePath);
if (fileInfo.Attributes.HasFlag(FileAttributes.Directory))
{
RenameDirectory(sourceFilePath, outputFilePath);
}
else
{
fileInfo.MoveTo(outputFilePath);
}
}
/// <summary>
/// 確実にディレクトリの名前を変更する
/// </summary>
/// <param name="sourceFilePath">変更元ファイルパス</param>
/// <param name="outputFilePath">変更後ファイルパス</param>
public static void RenameDirectory(string sourceFilePath, string outputFilePath)
{
//Directory.Moveはなぜか、大文字小文字だけの変更だとエラーする
//なので、大文字小文字だけの変更の場合は一度別のファイル名に変更する
if ((String.Compare(sourceFilePath, outputFilePath, true) == 0))
{
var tempPath = GetSafeTempName(outputFilePath);
Directory.Move(sourceFilePath, tempPath);
Directory.Move(tempPath, outputFilePath);
}
else
{
Directory.Move(sourceFilePath, outputFilePath);
}
}
/// <summary>
/// 指定したファイルパスが他のファイルパスとかぶらなくなるまで"_"を足して返す
/// </summary>
private static string GetSafeTempName(string outputFilePath)
{
outputFilePath += "_";
while (File.Exists(outputFilePath))
{
outputFilePath += "_";
}
return outputFilePath;
}
参考
環境
VisualStudio2019
.NET Core 3.1
C#8