यदि फ़ोल्डर खाली (.NET) है तो जल्दी से कैसे जांचें?


140

मुझे जांचना होगा, अगर डिस्क पर निर्देशिका खाली है। इसका मतलब है, कि इसमें कोई फोल्डर / फाइल नहीं है। मुझे पता है, कि एक सरल विधि है। हम FileSystemInfo की सरणी प्राप्त करते हैं और जांचते हैं कि तत्वों की गिनती शून्य के बराबर है या नहीं। ऐसा कुछ:

public static bool CheckFolderEmpty(string path)
{
    if (string.IsNullOrEmpty(path))
    {
        throw new ArgumentNullException("path");
    }

    var folder = new DirectoryInfo(path);
    if (folder.Exists)
    {
        return folder.GetFileSystemInfos().Length == 0;
    }

    throw new DirectoryNotFoundException();
}

यह दृष्टिकोण ठीक लगता है। परंतु!! यह प्रदर्शन के दृष्टिकोण से बहुत खराब है। GetFileSystemInfos () एक बहुत ही कठिन विधि है। वास्तव में, यह फ़ोल्डर के सभी फाइल सिस्टम ऑब्जेक्ट्स को एन्यूमरेट करता है, उनके सभी गुणों को प्राप्त करता है, ऑब्जेक्ट बनाता है, टाइप किए गए एरे को भरता है और यह सब सिर्फ लंबाई की जांच करने के लिए है। यह बेवकूफ है, है ना?

मैंने ऐसे कोड को केवल निर्धारित किया और निर्धारित किया, कि ~ 500ms में इस तरह की ~ 250 कॉल निष्पादित की जाती हैं। यह बहुत धीमा है और मुझे विश्वास है, कि यह बहुत जल्दी करना संभव है।

कोई सुझाव?


7
जिज्ञासा से बाहर, आप 250 बार निर्देशिका की जांच क्यों करना चाहेंगे?
यई २३

2
@ ya23 मुझे लगता है कि एक 250 विभिन्न निर्देशिकाओं की जाँच करना चाहेगा। 250 बार एक भी नहीं।
मैथ्यू पैग

जवाबों:


282

वहाँ में एक नई सुविधा है Directoryऔर DirectoryInfoनेट 4 में है कि उन्हें एक वापस जाने के लिए अनुमति देता है IEnumerableएक सरणी के बजाय, और सभी निर्देशिका सामग्री पढ़ने से पहले परिणाम देने शुरू करते हैं।

public bool IsDirectoryEmpty(string path)
{
    IEnumerable<string> items = Directory.EnumerateFileSystemEntries(path);
    using (IEnumerator<string> en = items.GetEnumerator())
    {
        return !en.MoveNext();
    }
}

संपादित करें: उस उत्तर को फिर से देखते हुए, मुझे लगता है कि इस कोड को बहुत सरल बनाया जा सकता है ...

public bool IsDirectoryEmpty(string path)
{
    return !Directory.EnumerateFileSystemEntries(path).Any();
}

मुझे यह समाधान पसंद है, क्या इसे केवल कुछ फ़िल्टरों के लिए जांचने के लिए बनाया जा सकता है? .Contains ("jpg") के बजाय .any () काम करने के लिए प्रतीत नहीं हुआ
डेनिस

5
@ डेनिस, आप कॉल में वाइल्डकार्ड पैटर्न निर्दिष्ट कर सकते हैं EnumerateFileSystemEntriesया उपयोग कर सकते हैं .Any(condition)(स्थिति को लंबोदर अभिव्यक्ति के रूप में निर्दिष्ट कर सकते हैं, या एक विधि के रूप में जो एक पैरामीटर के रूप में एक पथ लेता है)।
थॉमस लेवेस्क

टाइपकास्ट को पहले कोड उदाहरण से हटाया जा सकता है:return !items.GetEnumerator().MoveNext();
गैरी

1
@ गैरी, यदि आप ऐसा करते हैं, तो एन्यूमरेटर को निपटाया नहीं जाएगा, इसलिए यह डायरेक्ट्री को तब तक लॉक करेगा जब तक एन्यूमरेटर कचरा इकट्ठा नहीं हो जाता।
थॉमस लेवेस्क

ऐसा लगता है कि फ़ाइल युक्त निर्देशिकाओं के लिए यह ठीक काम करता है, लेकिन यदि निर्देशिका में अन्य निर्देशक शामिल हैं, तो यह खाली है यह कहते हुए वापस आता है।
कैरन

32

यहाँ अतिरिक्त तेज़ समाधान है, जिसे मैंने अंततः लागू किया। यहाँ मैं उपयोग कर रहा हूँ WinAPI और कार्यों FindFirstFile , FindNextFile । यह फ़ोल्डर में सभी वस्तुओं की गणना से बचने की अनुमति देता है और फ़ोल्डर में पहली वस्तु का पता लगाने के तुरंत बाद बंद हो जाता है । यह दृष्टिकोण ऊपर वर्णित की तुलना में ~ 6 (!!) गुना अधिक तेज है। 36ms में 250 कॉल!

private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WIN32_FIND_DATA
{
    public uint dwFileAttributes;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
    public uint nFileSizeHigh;
    public uint nFileSizeLow;
    public uint dwReserved0;
    public uint dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string cFileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
    public string cAlternateFileName;
}

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

[DllImport("kernel32.dll")]
private static extern bool FindClose(IntPtr hFindFile);

public static bool CheckDirectoryEmpty_Fast(string path)
{
    if (string.IsNullOrEmpty(path))
    {
        throw new ArgumentNullException(path);
    }

    if (Directory.Exists(path))
    {
        if (path.EndsWith(Path.DirectorySeparatorChar.ToString()))
            path += "*";
        else
            path += Path.DirectorySeparatorChar + "*";

        WIN32_FIND_DATA findData;
        var findHandle = FindFirstFile(path, out findData);

        if (findHandle != INVALID_HANDLE_VALUE)
        {
            try
            {
                bool empty = true;
                do
                {
                    if (findData.cFileName != "." && findData.cFileName != "..")
                        empty = false;
                } while (empty && FindNextFile(findHandle, out findData));

                return empty;
            }
            finally
            {
                FindClose(findHandle);
            }
        }

        throw new Exception("Failed to get directory first file",
            Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
    }
    throw new DirectoryNotFoundException();
}

मुझे उम्मीद है कि यह भविष्य में किसी के लिए उपयोगी होगा।


अपना समाधान साझा करने के लिए धन्यवाद।
ग्रेग

3
आप जोड़ने की जरूरत है SetLastError = trueके लिए DllImportके लिए FindFirstFileके लिए क्रम में Marshal.GetHRForLastWin32Error()की टिप्पणियां खंड में वर्णित के रूप में, सही ढंग से काम करने के लिए कॉल () GetHRForLastWin32Error के लिए MSDN दस्तावेज़
जोएल वी। अर्नेस्ट-डीयॉन्ग

मुझे लगता है कि निम्नलिखित उत्तर थोड़ा बेहतर है क्योंकि यह उप निर्देशिका stackoverflow.com/questions/724148/…
मयंक

21

आप कोशिश कर सकते हैं Directory.Exists(path)और Directory.GetFiles(path)- शायद कम ओवरहेड (कोई वस्तु नहीं - बस तार आदि)।


हमेशा की तरह, आप ट्रिगर से सबसे तेज़ हैं! मुझे वहाँ कुछ सेकंड से हराया! :-)
सेरेब्रस

तुम दोनों मुझसे ज्यादा तेज थे ... मेरे ध्यान पर विस्तार से;;
इयोन कैंपबेल

2
हालांकि मुझे कोई अच्छा नहीं लगा; पहला उत्तर है, और एक वोट के बिना ;-( केवल एक
मार्क Gravell

Unfixed ... किसी को पीसने के लिए एक कुल्हाड़ी, मुझे मालूम होता है
मार्क Gravell

1
मुझे नहीं लगता कि GetFiles को निर्देशिकाओं की एक सूची मिलेगी, इसलिए यह GetDirectories के लिए एक चेक में डालने के लिए एक अच्छा विचार प्रतीत होता है
केयर्न

18
private static void test()
{
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    string [] dirs = System.IO.Directory.GetDirectories("C:\\Test\\");
    string[] files = System.IO.Directory.GetFiles("C:\\Test\\");

    if (dirs.Length == 0 && files.Length == 0)
        Console.WriteLine("Empty");
    else
        Console.WriteLine("Not Empty");

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}

यह त्वरित परीक्षण फ़ोल्डर के लिए 2 मिलीसेकंड में वापस आया जब खाली और जब सबफ़ोल्डर्स और फ़ाइलें (प्रत्येक में 5 फ़ाइलों के साथ 5 फ़ोल्डर)


3
यदि फ़ाइलों की सूची प्राप्त किए बिना, 'dirs' सीधे खाली नहीं है, तो आप इसे वापस करके सुधार कर सकते हैं।
समजसन

3
हां, लेकिन क्या होगा अगर उसमें हजारों फाइलें हों?
थॉमस लेवेस्क

3
आप कंसोल को लिखने का समय भी माप रहे हैं, जो नगण्य नहीं है।
ctusch

11

मैं इसका उपयोग फ़ोल्डर्स और फ़ाइलों के लिए करता हूं (नहीं पता कि यह इष्टतम है)

    if(Directory.GetFileSystemEntries(path).Length == 0)

8

यदि आपको शुद्ध C # छोड़ने और WinApi कॉल के लिए जाने का मन नहीं है , तो आप PathIsDirectoryEmpty () फ़ंक्शन पर विचार करना चाह सकते हैं । MSDN के अनुसार, फ़ंक्शन:

यदि TRZ एक खाली निर्देशिका है, तो TRUE लौटाता है। यदि FALSE pszPath निर्देशिका नहीं है, या यदि इसमें "के अलावा कम से कम एक फ़ाइल शामिल है, तो लौटाता है।" या ".."।

यह एक ऐसा कार्य प्रतीत होता है जो वास्तव में वही करता है जो आप चाहते हैं, इसलिए यह संभवतः उस कार्य के लिए अच्छी तरह से अनुकूलित है (हालांकि मैंने इसका परीक्षण नहीं किया है)।

इसे C # से कॉल करने के लिए, pinvoke.net साइट को आपकी मदद करनी चाहिए। (दुर्भाग्य से, यह अभी तक इस निश्चित फ़ंक्शन का वर्णन नहीं करता है, लेकिन आपको कुछ कार्यों को समान तर्कों और रिटर्न प्रकार के साथ खोजने में सक्षम होना चाहिए और उन्हें अपने कॉल के आधार के रूप में उपयोग करना चाहिए। यदि आप MSDN में फिर से देखते हैं, तो यह कहता है कि से आयात करने के लिए DLL है shlwapi.dll)


महान विचार। मैं इस समारोह के बारे में नहीं जानता था। मैं अपने दृष्टिकोण के साथ प्रदर्शन की तुलना करने की कोशिश करूँगा, जो मैंने ऊपर वर्णित किया है। यदि यह तेज़ी से होता, तो मैं इसे अपने कोड में पुनः उपयोग करूँगा। धन्यवाद।
ज़े

4
जो लोग इस मार्ग पर जाना चाहते हैं उनके लिए एक नोट। ऐसा लगता है कि इस PathIsDirectoryEmpty () विधि से shlwapi.dll Vista32 / 64 और XP32 / 64 मशीनों पर ठीक काम करता है, लेकिन कुछ Win7 मशीनों पर बमबारी करता है। यह विंडोज के विभिन्न संस्करणों के साथ शिप किए गए shlwapi.dll के संस्करणों के साथ कुछ करना होगा। खबरदार।
एलेक्स_पी

7

मुझे इस पर प्रदर्शन के आँकड़ों के बारे में पता नहीं है, लेकिन क्या आपने Directory.GetFiles()स्थैतिक पद्धति का उपयोग करने की कोशिश की है ?

यह फ़ाइल नाम (नहीं FileInfos) युक्त एक स्ट्रिंग सरणी देता है और आप ऊपर की तरह ही सरणी की लंबाई की जांच कर सकते हैं।


एक ही मुद्दा है, अगर यह बहुत सारी फाइलें हैं तो यह धीमा हो सकता है ... लेकिन यह शायद तेज है कि GetFileSystemInfos
थॉमस लेवेस्क

4

मुझे यकीन है कि अन्य उत्तर तेज़ हैं, और आपके प्रश्न के लिए कहा गया है कि क्या फ़ोल्डर में फ़ाइलें या फ़ोल्डर हैं या नहीं ... लेकिन मुझे लगता है कि अधिकांश समय लोग किसी निर्देशिका को खाली मानेंगे यदि इसमें कोई फ़ाइल नहीं है। अगर यह खाली उपनिर्देशिका सम्‍मिलित है तो मेरे लिए यह अभी भी "खाली" है ... यह आपके उपयोग के लिए फिट नहीं हो सकता है, लेकिन दूसरों के लिए हो सकता है!

  public bool DirectoryIsEmpty(string path)
  {
    int fileCount = Directory.GetFiles(path).Length;
    if (fileCount > 0)
    {
        return false;
    }

    string[] dirs = Directory.GetDirectories(path);
    foreach (string dir in dirs)
    {
      if (! DirectoryIsEmpty(dir))
      {
        return false;
      }
    }

    return true;
  }

Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any()
जोनाथन गिल्बर्ट

3

आपको किसी भी मामले में इस जानकारी के लिए हार्ड ड्राइव पर जाना होगा, और यह अकेले किसी भी ऑब्जेक्ट निर्माण और सरणी भरने को ट्रम्प करेगा।


1
सच है, हालांकि कुछ वस्तुओं को बनाने में डिस्क पर अतिरिक्त मेटाडेटा को शामिल करना शामिल है जो आवश्यक नहीं हो सकता है।
एडम रोसेनफील्ड

एसीएल सुनिश्चित करने के लिए हर वस्तु के लिए आवश्यक होगा। इसके आसपास कोई रास्ता नहीं है। और एक बार जब आप उन लोगों को देखना चाहते हैं, तो आप फ़ोल्डर में फ़ाइलों के लिए एमएफटी हेडर में किसी भी अन्य जानकारी को पढ़ सकते हैं।
डॉन रेबा

3

मुझे ऐसी विधि के बारे में पता नहीं है, जो आपको बताएगी कि क्या किसी दिए गए फ़ोल्डर में कोई अन्य फ़ोल्डर या फाइलें हैं, हालांकि, इसका उपयोग करते हुए:

Directory.GetFiles(path);
&
Directory.GetDirectories(path);

इन दोनों तरीकों से प्रदर्शन में मदद करनी चाहिए, केवल फाइल / निर्देशिका के नामों के साथ स्ट्रिंग के एक सरणी को लौटाएंगे बजाय पूरे FileSystem /fo ऑब्जेक्ट्स के।


2

धन्यवाद, हर कोई, उत्तर के लिए। मैंने Directory.GetFiles () और Directory.GetDirectories () विधियों का उपयोग करने का प्रयास किया । खुशखबरी! प्रदर्शन में सुधार ~ दो बार! 221ms में 229 कॉल। लेकिन मुझे यह भी उम्मीद है, कि फ़ोल्डर में सभी वस्तुओं की गणना से बचना संभव है। सहमत हूँ, कि अभी भी अनावश्यक कार्य निष्पादित कर रहा है। क्या आपको ऐसा नहीं लगता?

सभी जांच के बाद, मैं एक निष्कर्ष पर पहुंचा, कि शुद्ध .NET के तहत आगे अनुकूलन असंभव है। मैं WinAPI के FindFirstFile फ़ंक्शन के साथ खेलने जा रहा हूं । आशा है कि यह मदद करेगा।


1
ब्याज से बाहर, इस ऑपरेशन के लिए आपको ऐसे उच्च प्रदर्शन की क्या आवश्यकता है?
मेन्डमाइकोड

1
अपने स्वयं के प्रश्न का उत्तर देने के बजाय, सही उत्तरों में से किसी एक को उत्तर के रूप में चिह्नित करें (शायद पहले वाला पोस्ट या सबसे स्पष्ट एक)। इस तरह से stackoverflow के भविष्य के उपयोगकर्ता आपके प्रश्न के तहत सही उत्तर देखेंगे!
रे हेस

2

कुछ समय आप यह सत्यापित करना चाह सकते हैं कि क्या कोई फाइल उप निर्देशिकाओं के अंदर मौजूद है और उन खाली उप निर्देशिकाओं को अनदेखा करें; इस मामले में आप नीचे दी गई विधि का उपयोग कर सकते हैं:

public bool isDirectoryContainFiles(string path) {
    if (!Directory.Exists(path)) return false;
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any();
}


0

ब्रैड पार्क्स कोड के आधार पर :

    public static bool DirectoryIsEmpty(string path)
    {
        if (System.IO.Directory.GetFiles(path).Length > 0) return false;

        foreach (string dir in System.IO.Directory.GetDirectories(path))
            if (!DirectoryIsEmpty(dir)) return false;

        return true;
    }

-1

मेरा कोड अद्भुत है यह सिर्फ 00: 00: 00.0007143 फ़ोल्डर में 34 फ़ाइल के साथ मिलिसकॉन्ड से कम है

   System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

     bool IsEmptyDirectory = (Directory.GetFiles("d:\\pdf").Length == 0);

     sw.Stop();
     Console.WriteLine(sw.Elapsed);

वास्तव में, यदि आप इसे 229 से गुणा करते हैं और गेटडायरेक्टरीज () जोड़ते हैं, तो आपको वही परिणाम मिलेगा, जैसे मेरा :)
zhe

-1

यहाँ कुछ ऐसा है जो आपको इसे करने में मदद कर सकता है। मैं इसे दो पुनरावृत्तियों में करने में कामयाब रहा।

 private static IEnumerable<string> GetAllNonEmptyDirectories(string path)
   {
     var directories =
        Directory.EnumerateDirectories(path, "*.*", SearchOption.AllDirectories)
        .ToList();

     var directoryList = 
     (from directory in directories
     let isEmpty = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories).Length == 0
     where !isEmpty select directory)
     .ToList();

     return directoryList.ToList();
   }

-1

चूंकि आप वैसे भी एक DirectoryInfo ऑब्जेक्ट के साथ काम करने जा रहे हैं, मैं एक एक्सटेंशन के साथ जाऊंगा

public static bool IsEmpty(this DirectoryInfo directoryInfo)
{
    return directoryInfo.GetFileSystemInfos().Count() == 0;
}

-2

इसे इस्तेमाल करो। यह आसान है।

Public Function IsDirectoryEmpty(ByVal strDirectoryPath As String) As Boolean
        Dim s() As String = _
            Directory.GetFiles(strDirectoryPath)
        If s.Length = 0 Then
            Return True
        Else
            Return False
        End If
    End Function

2
सरल, शायद। लेकिन गलत है। इसके दो प्रमुख बग हैं: यह पता नहीं लगाता है कि क्या कोई फ़ोल्डर रास्ते में है, केवल फाइलें हैं, और यह एक ऐसे अपवाद को फेंक देगा जो मौजूद नहीं है। यह वास्तव में ओपी के मूल की तुलना में धीमा होने की संभावना है , क्योंकि मुझे पूरा यकीन है कि यह सभी प्रविष्टियों को प्राप्त करता है और उन्हें फ़िल्टर करता है।
एंड्रयू बार्बर 19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.