كيف يمكنني تحديد ما إذا كان نظام الملفات حساسة لحالة الأحرف في .الشبكة ؟

StackOverflow https://stackoverflow.com/questions/430256

سؤال

لا .صافي طريقة لتحديد ما إذا كانت الملفات المحلية حالة حساسة ؟

هل كانت مفيدة؟

المحلول

يمكنك إنشاء ملف في المجلد المؤقت Temp (باستخدام اسم صغيرة)، ثم معرفة ما اذا كان الملف موجود (باستخدام اسم الملف الكبير)، منها مثلا:

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

نصائح أخرى

وليس هناك مثل هذه وظيفة في مكتبة الفئات .NET.

ويمكنك، ومع ذلك، طرح بنفسك: حاول إنشاء ملف باسم صغيرة ثم حاول فتحه مع الإصدار upparcase من اسمها. ربما كان من الممكن تحسين هذه الطريقة، ولكن تحصل على هذه الفكرة.

تعديل : لهل يمكن في الواقع مجرد أخذ الملف الأول في الدليل الجذر ومن ثم معرفة ما اذا كان كل من filename.ToLower () وfilename.ToUpper () موجودة. ومما يؤسف له أنه من الممكن تماما أن كلا الخيارين الكبيرة والصغيرة من نفس الملف موجودة، لذلك يجب عليك مقارنة خصائص FileInfo.Name كل من الصغيرة والكبيرة متغيرات لمعرفة ما إذا هم في الواقع نفس أم لا. وهذا لا يتطلب الكتابة إلى القرص.

ومن الواضح أن هذا سوف تفشل إذا كان هناك أية ملفات على الإطلاق على وحدة التخزين. في هذه الحالة، فقط تراجع إلى الخيار الأول (انظر الجواب مارتن لتنفيذ).

ونضع في اعتبارنا أن قد يكون لديك أنظمة الملفات متعددة مع قواعد غلاف مختلفة. على سبيل المثال، يمكن للنظام ملفات الجذر يكون لحالة الأحرف، ولكن هل يمكن أن يكون نظام الملفات تحسس حالة الأحرف (مثل عصا USB مع نظام الملفات FAT على ذلك) شنت في مكان ما. حتى إذا كنت تفعل مثل هذه الشيكات والتأكد من أن لجعلها في الدليل الذي كنت تسير على الوصول.

وأيضا، ماذا لو نسخ المستخدم البيانات من يقول لحالة الأحرف إلى نظام ملفات تحسس حالة الأحرف؟ إذا كان لديك الملفات التي تختلف فقط من قبل الحالة، واحد منهم الكتابة فوق الآخر، مما تسبب في فقدان البيانات. عند نسخ في الاتجاه الآخر، قد أيضا تشغيل في مشاكل، على سبيل المثال، إذا كان الملف A يحتوي على إشارة إلى ملف "ب"، ولكن هو في الواقع تسمية الملف "B". وهذا يعمل على نظام الملفات تحسس حالة الأحرف الأصلية، ولكن ليس على النظام لحالة الأحرف.

وهكذا أود أن أقترح عليك أن تجنب هذا يتوقف على ما إذا كان نظام الملفات لحالة الأحرف أو لا إذا كنت تستطيع. لا تولد أسماء الملفات التي تختلف فقط من قبل الحالة، استخدم الحوارات ملف منقار القياسية، ويكون مستعدا أن القضية قد تغير، وما إلى ذلك.

وحاول إنشاء ملف مؤقت في كل صغيرة، ومن ثم معرفة ما اذا كان موجودا باستخدام أحرف كبيرة.

وانها ليست وظيفة .NET، ولكن وظائف GetVolumeInformation وGetVolumeInformationByHandleW من Windows API تفعل ما تريد (انظر lpFileSystemFlags yje المعلمة.

في الواقع هناك طريقتان لتفسير السؤال الأصلي.

  1. كيفية تحديد ما إذا كان ملف معين غير قادرة على الحفاظ على حالة الأحرف في أسماء الملفات?
  2. كيفية تحديد ما إذا كان نظام التشغيل الحالي يفسر أسماء الملفات القضية بحساسية عند العمل مع ملف معين النظام.

هذا الجواب بناء على التفسير الثاني ، لأنني أعتقد أن هذا هو ما OP أريد أن أعرف أيضا ما يهم معظم الناس.

التعليمة البرمجية التالية مبني على M4N و نيكولا راؤول الجواب ومحاولات لخلق حقا قوة التنفيذ التي هي قادرة على تحديد ما إذا كان نظام التشغيل يتعامل مع أسماء الملفات حساسة لحالة الأحرف داخل الدليل المحدد (باستثناء الدلائل الفرعية ، لأن هذه يمكن أن تكون محمولة من آخر نظام الملفات).

وهو يعمل عن طريق إنشاء ملفات جديدة في خلافة واحد مع أقل حدة ، والآخر مع العلوي-حالة الأحرف.تم تأمين الملفات حصرا ويتم حذفها تلقائيا عند إغلاق.هذا ينبغي تجنب أي آثار جانبية سلبية الناجمة عن إنشاء ملفات.بالطبع, هذا التطبيق يعمل فقط إذا كان الدليل المحدد موجود الحالي المستخدم قادرا على إنشاء ملفات داخله.

يتم كتابة التعليمات البرمجية ل .NET Framework 4.0 و C# 7.2 (أو في وقت لاحق).

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

كما ترون هناك إمكانية ضئيلة للحصول على شرط سباق الذي يمكن أن يسبب سلبية كاذبة.إذا كان هذا السباق الشرط هو شيء كنت حقا تقلق أقترح عليك أن تفعل الاختيار مرة ثانية عندما تكون النتيجة كاذبة ، إما داخل IsFileSystemCaseSensitive طريقة أو خارجها.ومع ذلك ، في رأيي ، فإن احتمال مواجهة حالة السباق هذه مرة واحدة ، ناهيك مرتين في صف واحد ، فلكية صغيرة.

وأنا استدعاء والغش:

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

على أساس M4N الجواب ، مع التغييرات التالية:

  • أسماء ثابت بحيث نحن على يقين من أنه يحتوي على رسالة وليس فقط أرقام.
  • ربما أكثر قابلية للقراءة ؟
  • ملفوفة في الأسلوب.
  • وثائق.

أفضل استراتيجية هي أن تأخذ مسار كحجة ، و إنشاء الملف على نفس الملفات ولكن الكتابة قد يكون لها عواقب غير متوقعة.

وماذا عن هذا الكشف عن مجريات الأمور؟

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

وبدلا من ذلك، فإنه يحتوي على المعرفة الراسخة حول بيئات التشغيل.

ومتاح بسهولة كحزمة NuGet، ويعمل على كل شيء. NET 4.0+ وتحديثها على أساس منتظم: <لأ href = "https://github.com/gapotchenko/Gapotchenko.FX/tree/master/Source/Gapotchenko. FX.IO # iscasesensitive "يختلط =" نوفولو noreferrer "> https://github.com/gapotchenko/Gapotchenko.FX/tree/master/Source/Gapotchenko.FX.IO#iscasesensitive

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top