Frage

Hat .net einen Weg, um zu bestimmen, ob das lokale Dateisystem ist case-sensitive?

War es hilfreich?

Lösung

Sie können eine Datei im temporären Ordner erstellen (Kleinbuchstaben Dateinamen verwenden), dann überprüfen Sie, ob die Datei (mit Großdateiname) vorhanden ist, z:

string file = Path.GetTempPath() + Guid.NewGuid().ToString().ToLower();
File.CreateText(file).Close();
bool isCaseInsensitive = File.Exists(file.ToUpper());
File.Delete(file);

Andere Tipps

Es gibt keine solche Funktion in der .NET-Klassenbibliothek.

Sie können jedoch ausrollen Ihre eigenen Versuchen Sie eine Datei mit einem Klein Namen erstellen und dann versuchen Sie es mit der upparcase Version seines Namens zu öffnen. Wahrscheinlich ist es möglich, dieses Verfahren zu verbessern, aber Sie bekommen die Idee.

Bearbeiten : Sie könnte tatsächlich nehmen Sie nur die erste Datei in das Root-Verzeichnis und dann prüfen, ob beide filename.ToLower () und filename.ToUpper () vorhanden sein. Leider ist es durchaus möglich, dass sowohl Groß- und Klein Varianten derselben Datei vorhanden ist, so dass Sie die FileInfo.Name Eigenschaften sowohl die Klein- und Groß Varianten vergleichen sollten, um zu sehen, ob sie in der Tat gleich sind oder nicht. Dies wird nicht benötigt, um die Festplatte geschrieben werden.

Offensichtlich wird dies scheitern, wenn es keine Dateien überhaupt auf dem Volumen. In diesem Fall fällt nur zurück auf die erste Option (Martin Antwort für die Umsetzung sehen).

Beachten Sie, dass Sie mehrere Dateisysteme mit unterschiedlichen Gehäusen Regeln haben könnten. Zum Beispiel könnte das Root-Dateisystem case-sensitive sein, aber Sie können ein Groß- und Kleinschreibung Dateisystem (zum Beispiel eines USB-Stick mit einem FAT-Dateisystem darauf) montiert irgendwo haben. Also, wenn Sie solche Kontrollen zu tun, stellen Sie sicher, dass Sie sie in dem Verzeichnis, die Sie für den Zugriff werden.

Auch was ist, wenn der Benutzer kopiert die Daten von etwa einem Fall empfindlich gegenüber Groß- und Kleinschreibung Dateisystem? Wenn Sie Dateien, die nur von Fall zu unterscheiden, von denen die andere überschreiben, Datenverlust verursachen. Wenn in der anderen Richtung zu kopieren, können Sie auch auf Probleme stoßen, zum Beispiel, wenn die Datei A einen Verweis enthält „b“ in Datei, aber die Datei ist eigentlich „B“ genannt. Dies funktioniert auf dem ursprünglichen Groß- und Kleinschreibung Dateisystem, aber nicht auf dem Fall empfindliches System.

So würde ich vorschlagen, dass Sie, ob das Dateisystem in Abhängigkeit von vermeiden wird die Groß- oder nicht, wenn Sie können. Keine Dateinamen erzeugen, die nur von Fall unterscheiden, verwenden Sie die Standard-Dialoge Dateiauswahl, vorbereitet sein, dass der Fall könnte sich ändern, etc.

Versuchen Sie, eine temporäre Datei in Kleinbuchstaben zu schaffen, und dann prüfen, ob es existiert Groß verwendet wird.

Es ist nicht eine .NET-Funktion, aber die GetVolumeInformation und GetVolumeInformationByHandleW Funktionen aus dem Windows-API werden tun, was Sie (siehe yje lpFileSystemFlags Parameter werden sollen.

Es gibt zwei Möglichkeiten, um die ursprüngliche Frage zu interpretieren.

  1. Wie um zu bestimmen, ob ein bestimmtes Dateisystem ist in der Lage Groß- und Klein in Dateinamen zu erhalten?
  2. Wie um zu bestimmen, ob das aktuelle Betriebssystem Dateinamen zwischen Groß- und sensibel interpretiert, wenn sie mit einem bestimmten Dateisystem arbeiten.

Diese Antwort ist auf der zweiten Interpretation basiert, weil ich denke, das ist, was die OP wissen wollte, und auch, was wichtig ist für die meisten Menschen.

Der folgende Code wird auf M4N des und Nicolas Raoul Antwort lose basiert und versucht, eine wirklich robuste Implementierung zu schaffen, die fähig ist, um zu bestimmen, ob das Betriebssystem übernimmt die Dateinamen Groß- und innerhalb eines bestimmten Verzeichnisses (ohne Unterverzeichnisse, da diese könnte aus einem anderen Dateisystem gemountet werden).

Es funktioniert durch die Schaffung von zwei neuen Dateien nacheinander, eines mit Klein, die andere mit Großbuchstaben. Die Dateien werden exklusiv gesperrt und werden automatisch gelöscht, wenn sie geschlossen sind. Dies sollte keine negativen Nebenwirkungen durch die Schaffung von Dateien verursacht abzuwenden. Natürlich funktioniert diese Implementierung nur, wenn das angegebene Verzeichnis existiert und der aktuelle Benutzer in der Lage, Dateien in der es zu erstellen.

Der Code wird für .NET Framework 4.0 geschrieben und C # 7.2 (oder höher).

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);
}

Wie Sie zu sehen, gibt es eine kleine Möglichkeit für eine Race-Bedingung ist, die ein falsch-negative verursachen können. Wenn diese Racebedingung etwas, das Ihnen wirklich Sorgen zu machen Ich schlage vor, tun Sie dem Scheck ein zweites Mal, wenn das Ergebnis falsch ist, entweder innerhalb der IsFileSystemCaseSensitive Verfahren oder außerhalb. Doch meiner Meinung nach ist die Wahrscheinlichkeit, dieses Rennen Bedingung einmal begegnen, geschweige denn zwei Mal in Folge lassen, ist astronomisch klein.

Ich rufe Der Betrüger:

Path.DirectorySeparatorChar == '\\' ? "I'm insensitive" : "I'm probably sensitive"
/// <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;
}

Basierend auf M4N Antwort, mit folgenden Änderungen:

  • Statische Namen, damit wir sind sicher, dass es einen Brief und nicht nur Zahlen enthält.
  • Vielleicht besser lesbar?
  • in einem Verfahren eingewickelt.
  • Dokumentation.

Eine bessere Strategie wäre es, einen Weg als Argument zu nehmen, und die Datei auf dem gleichen Dateisystem erstellen, aber es schreiben könnte unerwartete Folgen haben.

Wie wäre es diese Heuristik?

public static bool IsCaseSensitiveFileSystem() {
   var tmp = Path.GetTempPath();
   return !Directory.Exists(tmp.ToUpper()) || !Directory.Exists(tmp.ToLower());
}

Hier ist der Ansatz, nicht temporäre Dateien nicht verwendet:

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;
    }
}

Stattdessen enthält es ein tief verwurzeltes Wissen über Betriebsumgebungen.

Problemlos als NuGet-Paket zur Verfügung, arbeitet auf allem .NET 4.0 und höher und in regelmäßigen Abständen aktualisiert: https://github.com/gapotchenko/Gapotchenko.FX/tree/master/Source/Gapotchenko.FX.IO#iscasesensitive

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top