Каталог.Exists / File.Exists с учетом регистра
Есть ли способ сделать регистр чувствительным Directory.Exists
/ File.Exists
поскольку
Directory.Exists(folderPath)
а также
Directory.Exists(folderPath.ToLower())
оба возвращаются true
?
В большинстве случаев это не имеет значения, но я использую макрос, который, кажется, не работает, если путь не соответствует случаям на 100%.
6 ответов
Поскольку Directory.Exists использует FindFirstFile, который не учитывает регистр, нет. Но вы можете PInvoke FindFirstFileEx с дополнительным параметром Flags, установленным в FIND_FIRST_EX_CASE_SENSITIVE.
Попробуйте эту функцию:
public static bool FileExistsCaseSensitive(string filename)
{
string name = Path.GetDirectoryName(filename);
return name != null
&& Array.Exists(Directory.GetFiles(name), s => s == Path.GetFullPath(filename));
}
Обновить:
Как указано в комментариях, это проверяет только случаи в имени файла, а не в пути. Это связано с тем, что метод GetFullPath возвращает не исходный путь Windows с исходными регистрами, а копию пути из параметра.
Пример:
GetFullPath("c:\TEST\file.txt") -> "c:\TEST\file.txt"
GetFullPath("c:\test\file.txt") -> "c:\test\file.txt"
Все методы, которые я пробовал, работают одинаково: Fileinfo, DirectoryInfo.
Вот решение с использованием метода kernel32.dll:
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetLongPathName(
string path,
StringBuilder longPath,
int longPathLength
);
/// <summary>
/// Return true if file exists. Non case sensitive by default.
/// </summary>
/// <param name="filename"></param>
/// <param name="caseSensitive"></param>
/// <returns></returns>
public static bool FileExists(string filename, bool caseSensitive = false)
{
if (!File.Exists(filename))
{
return false;
}
if (!caseSensitive)
{
return true;
}
//check case
StringBuilder longPath = new StringBuilder(255);
GetLongPathName(Path.GetFullPath(filename), longPath, longPath.Capacity);
string realPath = Path.GetDirectoryName(longPath.ToString());
return Array.Exists(Directory.GetFiles(realPath), s => s == filename);
}
Основываясь на решении этого вопроса, я написал код ниже, который учитывает регистр для всего пути, кроме буквы Windows Drive:
static void Main(string[] args)
{
string file1 = @"D:\tESt\Test.txt";
string file2 = @"d:\Test\test.txt";
string file3 = @"d:\test\notexists.txt";
bool exists1 = Case_Sensitive_File_Exists(file1);
bool exists2 = Case_Sensitive_File_Exists(file2);
bool exists3 = Case_Sensitive_File_Exists(file3);
Console.WriteLine("\n\nPress any key...");
Console.ReadKey();
}
static bool Case_Sensitive_File_Exists(string filepath)
{
string physicalPath = GetWindowsPhysicalPath(filepath);
if (physicalPath == null) return false;
if (filepath != physicalPath) return false;
else return true;
}
Я скопировал код для GetWindowsPhysicalPath(string path)
из вопроса
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetLongPathName(string ShortPath, StringBuilder sb, int buffer);
[DllImport("kernel32.dll")]
static extern uint GetShortPathName(string longpath, StringBuilder sb, int buffer);
protected static string GetWindowsPhysicalPath(string path)
{
StringBuilder builder = new StringBuilder(255);
// names with long extension can cause the short name to be actually larger than
// the long name.
GetShortPathName(path, builder, builder.Capacity);
path = builder.ToString();
uint result = GetLongPathName(path, builder, builder.Capacity);
if (result > 0 && result < builder.Capacity)
{
//Success retrieved long file name
builder[0] = char.ToLower(builder[0]);
return builder.ToString(0, (int)result);
}
if (result > 0)
{
//Need more capacity in the buffer
//specified in the result variable
builder = new StringBuilder((int)result);
result = GetLongPathName(path, builder, builder.Capacity);
builder[0] = char.ToLower(builder[0]);
return builder.ToString(0, (int)result);
}
return null;
}
Обратите внимание, что единственная проблема, с которой я столкнулся при использовании этой функции, заключается в том, что буква диска всегда отображается в нижнем регистре. Пример: физический путь в Windows: D:\Test\test.txt
, GetWindowsPhysicalPath(string path)
функция возвращает d:\Test\test.txt
Вот относительно простой способ проверить, действительно ли существует каталог с указанным именем. Мне это нужно, потому что у меня есть возможность переименовать папку, и она сначала проверяет, не будет ли конфликта. Так, например, если я хочу переименовать папку "cars" в "Cars".
Это довольно прямолинейно. Если система сообщает, что папка существует, я просто вызываю GetDirectories для родительской папки и передаю имя каталога в качестве подстановочного знака (таким образом возвращая ровно 1 результат). Простое сравнение дает мне ответ.
static public bool DirExistsMatchCase(string path)
{
// If it definitely doesn't return false
if (!Directory.Exists(path)) return false;
// Figure out if the case (of the final part) is the same
string thisDir = Path.GetFileName(path);
string actualDir = Path.GetFileName(Directory.GetDirectories(Path.GetDirectoryName(path), thisDir)[0]);
return thisDir == actualDir;
}
Попробуйте эти 2 более простых варианта, которым не нужно использовать PInvoke, и верните логическое значение, допускающее обнуление (bool?). Я не эксперт в предметной области, поэтому я знаю, является ли это наиболее эффективным кодом, но он работает для меня.
Просто введите путь, и если результат равен нулю (HasValue = false), совпадение не найдено, если результат равен false, есть точное совпадение, в противном случае, если true, есть совпадение с разностным регистром.
Методы GetFiles, GetDirectories и GetDrives возвращают точный регистр, сохраненный в вашей файловой системе, так что вы можете использовать метод сравнения с учетом регистра.
NB: для случая, когда путь является точным приводом (например, @"C:\"), я должен использовать немного другой подход.
using System.IO;
class MyFolderFileHelper {
public static bool? FileExistsWithDifferentCase(string fileName)
{
bool? result = null;
if (File.Exists(fileName))
{
result = false;
string directory = Path.GetDirectoryName(fileName);
string fileTitle = Path.GetFileName(fileName);
string[] files = Directory.GetFiles(directory, fileTitle);
if (String.Compare(files[0], fileName, false) != 0)
result = true;
}
return result;
}
public static bool? DirectoryExistsWithDifferentCase(string directoryName)
{
bool? result = null;
if (Directory.Exists(directoryName))
{
result = false;
directoryName = directoryName.TrimEnd(Path.DirectorySeparatorChar);
int lastPathSeparatorIndex = directoryName.LastIndexOf(Path.DirectorySeparatorChar);
if (lastPathSeparatorIndex >= 0)
{
string baseDirectory = directoryName.Substring(lastPathSeparatorIndex + 1);
string parentDirectory = directoryName.Substring(0, lastPathSeparatorIndex);
string[] directories = Directory.GetDirectories(parentDirectory, baseDirectory);
if (String.Compare(directories[0], directoryName, false) != 0)
result = true;
}
else
{
//if directory is a drive
directoryName += Path.DirectorySeparatorChar.ToString();
DriveInfo[] drives = DriveInfo.GetDrives();
foreach(DriveInfo driveInfo in drives)
{
if (String.Compare(driveInfo.Name, directoryName, true) == 0)
{
if (String.Compare(driveInfo.Name, directoryName, false) != 0)
result = true;
break;
}
}
}
}
return result;
}
}
Если (относительный или абсолютный) путь вашего файла:
string AssetPath = "...";
Следующее гарантирует, что файл существует и имеет правильный корпус:
if(File.Exists(AssetPath) && Path.GetFullPath(AssetPath) == Directory.GetFiles(Path.GetDirectoryName(Path.GetFullPath(AssetPath)), Path.GetFileName(Path.GetFullPath(AssetPath))).Single())
{
}
Наслаждайтесь!