如何确定.net中的文件系统是否区分大小写?

如何确定.net中的文件系统是否区分大小写?,.net,linux,mono,filesystems,case-sensitive,.net,Linux,Mono,Filesystems,Case Sensitive,.net是否有办法确定本地文件系统是否区分大小写?尝试创建一个全小写的临时文件,然后使用大写检查它是否存在。在.net类库中没有这样的函数 但是,您可以自己展开:尝试使用小写名称创建一个文件,然后尝试使用其名称的upparcase版本打开它。也许改进这种方法是可能的,但你明白了 编辑:您实际上可以只获取根目录中的第一个文件,然后检查filename.ToLower()和filename.ToUpper()是否都存在。不幸的是,很可能存在同一文件的大写和小写变体,因此您应该比较小写和大写变体的Fi

.net是否有办法确定本地文件系统是否区分大小写?

尝试创建一个全小写的临时文件,然后使用大写检查它是否存在。

在.net类库中没有这样的函数

但是,您可以自己展开:尝试使用小写名称创建一个文件,然后尝试使用其名称的upparcase版本打开它。也许改进这种方法是可能的,但你明白了

编辑:您实际上可以只获取根目录中的第一个文件,然后检查filename.ToLower()和filename.ToUpper()是否都存在。不幸的是,很可能存在同一文件的大写和小写变体,因此您应该比较小写和大写变体的FileInfo.Name属性,看看它们是否确实相同。这不需要写入磁盘


显然,如果卷上根本没有文件,这将失败。在这种情况下,只需返回到第一个选项(请参阅Martin的答案了解实现)。

您可以在临时文件夹中创建一个文件(使用小写文件名),然后检查该文件是否存在(使用大写文件名),例如:


请记住,您可能有多个具有不同大小写规则的文件系统。例如,根文件系统可能区分大小写,但您可以在某处安装一个不区分大小写的文件系统(例如,一个带有FAT文件系统的U盘)。因此,如果您进行此类检查,请确保将它们放在要访问的目录中

此外,如果用户将数据从区分大小写的文件系统复制到不区分大小写的文件系统,会怎么样?如果文件仅因具体情况而异,则其中一个文件将覆盖另一个文件,从而导致数据丢失。当以另一个方向复制时,您也可能会遇到问题,例如,如果文件A包含对文件“b”的引用,但该文件实际上名为“b”。这适用于原始的不区分大小写的文件系统,但不适用于区分大小写的系统


因此,如果可以,我建议您避免依赖文件系统是否区分大小写。不要生成仅因大小写不同的文件名,使用标准的文件选择器对话框,做好可能更改大小写的准备,等等。

这不是.NET函数,但Windows API中的GetVolumeInformation和GetVolumeInformation ByHandlew函数将执行您想要的操作(请参阅yje lpFileSystemFlags参数)。

//
/// <summary>
/// Check whether the operating system is case-sensitive.
/// For instance on Linux you can have two files/folders called
//// "test" and "TEST", but on Windows the two can not coexist.
/// This method does not extend to mounted filesystems, which might have different properties.
/// </summary>
/// <returns>true if the operating system is case-sensitive</returns>
public static bool IsFileSystemCaseSensitive()
{
    // Actually try.
    string file = Path.GetTempPath() + Guid.NewGuid().ToString().ToLower() + "test";
    File.CreateText(file).Close();
    bool result = ! File.Exists(file.ToUpper());
    File.Delete(file);

    return result;
}
///检查操作系统是否区分大小写。 ///例如,在Linux上,您可以有两个名为 ////“测试”和“测试”,但在Windows上这两者不能共存。 ///此方法不扩展到已装入的文件系统,这些文件系统可能具有不同的属性。 /// ///如果操作系统区分大小写,则为true 公共静态bool文件系统区分大小写() { //实际上,试试看。 字符串文件=Path.GetTempPath()+Guid.NewGuid().ToString().ToLower()+“测试”; File.CreateText(File.Close(); bool result=!File.Exists(File.ToUpper()); 文件。删除(文件); 返回结果; }
根据M4N的回答,进行以下更改:

  • 静态名称,以便我们确定它包含字母而不仅仅是数字
  • 也许更具可读性
  • 用一种方法包装
  • 文件
更好的策略是将路径作为参数,并在同一文件系统上创建文件,但在其中写入可能会产生意外的后果。

我调用欺骗:

Path.DirectorySeparatorChar == '\\' ? "I'm insensitive" : "I'm probably sensitive"

实际上有两种方法来解释原始问题

  • 如何确定特定文件系统是否能够在文件名中保留区分大小写的功能
  • 如何确定当前操作系统在使用特定文件系统时是否区分大小写解释文件名
  • 这个答案基于第二种解释,因为我认为这是OP想要知道的,也是对大多数人来说很重要的

    以下代码大致基于M4N和Nicolas Raoul的答案,并试图创建一个真正健壮的实现,该实现能够确定操作系统是否在指定目录(不包括子目录,因为这些子目录可以从另一个文件系统装入)内处理区分大小写的文件名

    它通过连续创建两个新文件来工作,一个使用小写,另一个使用大写字符。文件以独占方式锁定,并在关闭时自动删除。这应避免创建文件所造成的任何负面影响。 当然,只有当指定的目录存在并且当前用户能够在其中创建文件时,此实现才有效

    该代码是为.NETFramework4.0和C#7.2(或更高版本)编写的

    使用系统;
    使用System.IO;
    运用系统反思;
    /// 
    ///检查操作系统是否处理指定目录中区分大小写的文件名。
    /// 
    ///要检查的目录的路径。
    ///一个值,指示操作系统是否处理指定目录中区分大小写的文件名。
    ///是空的。
    ///包含一个或多个无效字符。
    ///指定的目录不存在。
    ///当前用户没有对指定目录的写入权限。
    私有静态bool是区分大小写的文件系统(字符串directoryPath)
    {
    if(directoryPath==null)
    {
    抛出新ArgumentNullException(nameof(directoryPath));
    }
    while(true)
    {
    字符串fileNameLower=“.cstest.”+Guid.NewGuid().ToString();
    字符串fileNameUpper=fileNameLower.ToUpperInvariant();
    字符串filePathLower=Path.Combine(directoryPath,fileNameLower);
    string filePathUpper=Path.Combine(directoryPath,fileNameUpper);
    FileStream fileStreamLower=null;
    FileStream fileStreamUpper=null;
    
    Path.DirectorySeparatorChar == '\\' ? "I'm insensitive" : "I'm probably sensitive"
    
    using System;
    using System.IO;
    using System.Reflection;
    
    /// <summary>
    /// Check whether the operating system handles file names case-sensitive in the specified directory.
    /// </summary>
    /// <param name="directoryPath">The path to the directory to check.</param>
    /// <returns>A value indicating whether the operating system handles file names case-sensitive in the specified directory.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="directoryPath"/> is null.</exception>
    /// <exception cref="ArgumentException"><paramref name="directoryPath"/> contains one or more invalid characters.</exception>
    /// <exception cref="DirectoryNotFoundException">The specified directory does not exist.</exception>
    /// <exception cref="UnauthorizedAccessException">The current user has no write permission to the specified directory.</exception>
    private static bool IsFileSystemCaseSensitive(string directoryPath)
    {
        if (directoryPath == null)
        {
            throw new ArgumentNullException(nameof(directoryPath));
        }
    
        while (true)
        {
            string fileNameLower = ".cstest." + Guid.NewGuid().ToString();
            string fileNameUpper = fileNameLower.ToUpperInvariant();
    
            string filePathLower = Path.Combine(directoryPath, fileNameLower);
            string filePathUpper = Path.Combine(directoryPath, fileNameUpper);
    
            FileStream fileStreamLower = null;
            FileStream fileStreamUpper = null;
            try
            {
                try
                {
                    // Try to create filePathUpper to ensure a unique non-existing file.
                    fileStreamUpper = new FileStream(filePathUpper, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 4096, FileOptions.DeleteOnClose);
    
                    // After ensuring that it didn't exist before, filePathUpper must be closed/deleted again to ensure correct opening of filePathLower, regardless of the case-sensitivity of the file system.
                    // On case-sensitive file systems there is a tiny chance for a race condition, where another process could create filePathUpper between closing/deleting it here and newly creating it after filePathLower.
                    // This method would then incorrectly indicate a case-insensitive file system.
                    fileStreamUpper.Dispose();
                }
                catch (IOException ioException) when (IsErrorFileExists(ioException))
                {
                    // filePathUpper already exists, try another file name
                    continue;
                }
    
                try
                {
                    fileStreamLower = new FileStream(filePathLower, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 4096, FileOptions.DeleteOnClose);
                }
                catch (IOException ioException) when (IsErrorFileExists(ioException))
                {
                    // filePathLower already exists, try another file name
                    continue;
                }
    
                try
                {
                    fileStreamUpper = new FileStream(filePathUpper, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 4096, FileOptions.DeleteOnClose);
    
                    // filePathUpper does not exist, this indicates case-sensitivity
                    return true;
                }
                catch (IOException ioException) when (IsErrorFileExists(ioException))
                {
                    // fileNameUpper already exists, this indicates case-insensitivity
                    return false;
                }
            }
            finally
            {
                fileStreamLower?.Dispose();
                fileStreamUpper?.Dispose();
            }
        }
    }
    
    /// <summary>
    /// Determines whether the specified <see cref="IOException"/> indicates a "file exists" error.
    /// </summary>
    /// <param name="ioException">The <see cref="IOException"/> to check.</param>
    /// <returns>A value indicating whether the specified <see cref="IOException"/> indicates a "file exists" error.</returns>
    private static bool IsErrorFileExists(IOException ioException)
    {
        // https://referencesource.microsoft.com/mscorlib/microsoft/win32/win32native.cs.html#dd35d7f626262141
        const int ERROR_FILE_EXISTS = 0x50;
    
        // The Exception.HResult property's get accessor is protected before .NET 4.5, need to get its value via reflection.
        int hresult = (int)typeof(Exception)
            .GetProperty("HResult", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .GetValue(ioException, index: null);
    
        // https://referencesource.microsoft.com/mscorlib/microsoft/win32/win32native.cs.html#9f6ca3226ff8f9ba
        return hresult == unchecked((int)0x80070000 | ERROR_FILE_EXISTS);
    }
    
    public static bool IsCaseSensitiveFileSystem() {
       var tmp = Path.GetTempPath();
       return !Directory.Exists(tmp.ToUpper()) || !Directory.Exists(tmp.ToLower());
    }
    
    using System;
    using System.Runtime.InteropServices;
    
    static bool IsCaseSensitive()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
            RuntimeInformation.IsOSPlatform(OSPlatform.OSX))  // HFS+ (the Mac file-system) is usually configured to be case insensitive.
        {
            return false;
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            return true;
        }
        else if (Environment.OSVersion.Platform == PlatformID.Unix)
        {
            return true;
        }
        else
        {
           // A default.
           return false;
        }
    }