2010-04-26 13 views
5

मेरे पास प्रोग्राम है जो डेटाबेस को लिखता है जो फ़ोल्डर्स पूर्ण या खाली होते हैं। अब मैंजांच कर रहा है कि फ़ोल्डर में फाइलें हैं

bool hasFiles=false; 
(Directory.GetFiles(path).Length >0) ? hasFiles=true: hasFiles=false; 

लेकिन इसमें लगभग एक घंटा लग रहा है, और मैं इस समय कुछ भी नहीं कर सकता।

क्या फ़ोल्डर में कोई फ़ाइल है या नहीं, यह जांचने का कोई सबसे तेज़ तरीका है?

+2

"यह" कितना समय लगता है? कोड की यह विशेष पंक्ति, या अपनी डिस्क पर हजारों निर्देशिकाओं पर लूप में इसका उपयोग कर रही है? –

+0

फ़ोल्डर में कितनी फाइलें हैं? –

+0

@ करल्सन प्रत्येक फ़ोल्डर में एक फ़ाइल है। – user278618

उत्तर

5

ऐसी क्रॉस-नेटवर्क खोज को तेज़ करने की कुंजी नेटवर्क पर अनुरोधों की संख्या को कम करना है। सभी निर्देशिकाएं प्राप्त करने के बजाय, और फिर फ़ाइलों के लिए प्रत्येक की जांच करें, कोशिश करें और एक कॉल से सबकुछ प्राप्त करें।

.NET 3.5 में सभी फ़ाइलों और फ़ोल्डर्स को रिकर्सिवली करने के लिए कोई भी तरीका नहीं है, इसलिए आपको इसे स्वयं बनाना होगा (नीचे देखें)। एक चरण में .NET 4 नए अधिभार में मौजूद है।

DirectoryInfo का उपयोग करके यह भी जानकारी प्राप्त करता है कि लौटाया गया नाम एक फ़ाइल या निर्देशिका है, जो कॉल को भी घटा देता है।

struct AllDirectories { 
    public List<string> DirectoriesWithoutFiles { get; set; } 
    public List<string> DirectoriesWithFiles { get; set; } 
} 

static class FileSystemScanner { 
    public AllDirectories DivideDirectories(string startingPath) { 
    var startingDir = new DirectoryInfo(startingPath); 

    // allContent IList<FileSystemInfo> 
    var allContent = GetAllFileSystemObjects(startingDir); 
    var allFiles = allContent.Where(f => !(f.Attributes & FileAttributes.Directory)) 
          .Cast<FileInfo>(); 
    var dirs = allContent.Where(f => (f.Attributes & FileAttributes.Directory)) 
         .Cast<DirectoryInfo>(); 
    var allDirs = new SortedList<DirectoryInfo>(dirs, new FileSystemInfoComparer()); 

    var res = new AllDirectories { 
     DirectoriesWithFiles = new List<string>() 
    }; 
    foreach (var file in allFiles) { 
     var dirName = Path.GetDirectoryName(file.Name); 
     if (allDirs.Remove(dirName)) { 
     // Was removed, so first time this dir name seen. 
     res.DirectoriesWithFiles.Add(dirName); 
     } 
    } 
    // allDirs now just contains directories without files 
    res.DirectoriesWithoutFiles = new List<String>(addDirs.Select(d => d.Name)); 
    } 

    class FileSystemInfoComparer : IComparer<FileSystemInfo> { 
    public int Compare(FileSystemInfo l, FileSystemInfo r) { 
     return String.Compare(l.Name, r.Name, StringComparison.OrdinalIgnoreCase); 
    } 
    } 
} 

को लागू करने GetAllFileSystemObjects नेट संस्करण पर निर्भर करता है:

इस बंटवारे सभी निर्देशिकाओं और फ़ाइलों की एक सूची का मतलब है कुछ इस तरह हो जाता है। पर ।नेट 4 यह बहुत आसान है:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) { 
    return root.GetFileSystemInfos("*.*", SearchOptions.AllDirectories); 
} 

पहले के संस्करणों पर थोड़ा और अधिक काम की जरूरत है:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) { 
    var res = new List<FileSystemInfo>(); 
    var pending = new Queue<DirectoryInfo>(new [] { root }); 

    while (pending.Count > 0) { 
    var dir = pending.Dequeue(); 
    var content = dir.GetFileSystemInfos(); 
    res.AddRange(content); 
    foreach (var dir in content.Where(f => (f.Attributes & FileAttributes.Directory)) 
           .Cast<DirectoryInfo>()) { 
     pending.Enqueue(dir); 
    } 
    } 

    return res; 
} 

यह दृष्टिकोण संभव के रूप में कई बार, बस एक बार नेट 4 पर या के रूप में फाइल सिस्टम में कॉल एक बार प्रति संस्करणों पर प्रति निर्देशिका, नेटवर्क क्लाइंट और सर्वर को अंतर्निहित फाइल सिस्टम कॉल और नेटवर्क राउंड ट्रिप की संख्या को कम करने की इजाजत देता है।

FileSystemInfo प्राप्त करने के उदाहरणों में एकाधिक फ़ाइल सिस्टम संचालन की आवश्यकता है (मुझे विश्वास है कि यह कुछ हद तक ओएस निर्भर है), लेकिन प्रत्येक नाम के लिए किसी भी समाधान को यह जानने की जरूरत है कि यह फ़ाइल या निर्देशिका है, तो यह कुछ स्तर पर टालने योग्य नहीं है (FindFileFirst/FindNextFile/FindClose के पी/Invoke का उपयोग किए बिना)।


अलावा, ऊपर एक विभाजन विस्तार विधि के साथ आसान होगा:

Tuple<IEnumerable<T>,IEnumerable<T>> Extensions.Partition<T>(
               this IEnumerable<T> input, 
               Func<T,bool> parition); 

लेखन कि आलसी एक दिलचस्प व्यायाम होगा होने के लिए (केवल इनपुट लेने जब आउटपुट में से एक पर कुछ दोहराता, जबकि दूसरे को बफरिंग)।

+0

इस तरह कुछ चाहिए लेकिन बस सोच रहा था। जब आप 'addDirs' चर का उपयोग करते हैं तो मुझे लगता है कि आपका मतलब' allDirs' था? या क्या मैं कुछ न कुछ भूल रहा हूं? – Niklas

+0

शायद निकलास। (लेकिन यह कुछ साल हो गया है ...) याद रखें कि आपको .NET 4 में इस कोड की आवश्यकता नहीं है क्योंकि यह फ़ाइलों और निर्देशिकाओं को दोबारा पढ़ सकता है। – Richard

3

मुझे लगता है (हालांकि मुझे निश्चित रूप से पता नहीं है) क्योंकि आप नेटवर्क ड्राइव पर GetFiles() को कॉल कर रहे हैं, यह सभी 30k फ़ोल्डर्स से सभी फ़ाइलों को पुनर्प्राप्त करने और उनके माध्यम से गणना करने के लिए काफी समय जोड़ता है।

मुझे कोडप्रोजेक्ट पर एक वैकल्पिक निर्देशिका अंकक here मिला है जो आशाजनक लग रहा है।

वैकल्पिक रूप से ... आप सर्वर पर एक वेब सेवा बना सकते हैं जो आपके लिए सबकुछ बताता है और परिणाम देता है।

संपादित करें: मुझे लगता है कि आपकी समस्या फ़ोल्डर पहुंच की अधिक संभावना है। प्रत्येक बार जब आप नेटवर्क ड्राइव में निर्देशिका एक्सेस करते हैं तो आप सुरक्षा और अनुमति जांच मारने जा रहे हैं। वह * 30k फ़ोल्डर्स एक बड़ा प्रदर्शन हिट होगा। मुझे FindFirstFile का उपयोग करने में अत्यधिक संदेह होगा, क्योंकि फाइलों की वास्तविक संख्या केवल 0 या 1.

0

आपकी सर्वश्रेष्ठ शर्त API फ़ंक्शन FindFirstFile का उपयोग करना है। यह लगभग तब तक नहीं ले जाएगा।

+1

प्रत्येक फ़ोल्डर में केवल एक फ़ाइल होती है; समस्या * लगता है * रिमोट * फ़ोल्डर्स * की विशाल संख्या होने के लिए, अनुक्रमिक रूप से पहुंचा। –

+0

+1 यहां एक चर्चा है जहां किसी को पता चलता है कि FindFirstfile निर्देशिकाओं की तुलना में बहुत तेज़ है। खाली निर्देशिकाओं की जांच के लिए गेटफाइल कोशिश करने लायक है: http://stackoverflow.com/questions/755574/how-to-quickly-check-if- फ़ोल्डर-खाली-खाली –

+1

मैं यहां मार्क के साथ समझौता कर रहा हूं। समस्या फाइलों की गणना नहीं कर रही है, यह सभी फ़ोल्डर संरचनाओं के माध्यम से गणना और कदम है। प्रत्येक बार .Net निर्देशिका पर GetFiles() को कॉल करता है, निर्देशिका के हर बार जब तक पहुंच का प्रयास किया जाता है, तो सुरक्षा जांच की एक श्रृंखला होने जा रही है। – GenericTypeTea

2

उल्लेख के लायक हो सकता है:

लेकिन यह लगभग एक घंटा लगता है, और मैं इस बार में कुछ नहीं कर सकते। (जोर जोड़ा गया)

क्या आप इसे मुख्य थ्रेड पर जीयूआई ऐप से कर रहे हैं? यदि ऐसा है, तो BackgroundWorker का उपयोग करके इस प्रक्रिया को बंद कर दें। कम से कम तब ऐप उत्तरदायी रहेगा। आप विधि में CancellationPending के लिए चेक भी जोड़ सकते हैं और इसे बहुत लंबा समय लेते हुए रद्द कर सकते हैं।

आपके प्रश्न के लिए टेंगेंशियल की तरह - केवल कुछ मैंने देखा और सोचा कि मैं टिप्पणी करूंगा।

4

यदि आप .NET 4.0 का उपयोग कर रहे हैं तो EnumerateFiles विधि पर एक नज़र डालें। http://msdn.microsoft.com/en-us/library/dd413232(v=VS.100).aspx

EnumerateFiles और GetFiles तरीके के रूप में अलग-अलग प्रकार है: जब आप उपयोग EnumerateFiles, आप पूरे संग्रह से पहले वस्तुओं FileInfo के संग्रह की गणना दिया जाता है शुरू कर सकते हैं; जब आप GetFiles का उपयोग करते हैं, तो आप को फ़ाइलइन्फो ऑब्जेक्ट्स को पूरी तरह से सरणी तक पहुंचने से पहले वापस लौटने की प्रतीक्षा करनी होगी। इसलिए, जब आप के साथ काम कर रहे हैं तो कई फ़ाइलें और निर्देशिकाएं, एन्युमेरेटफाइल अधिक कुशल हो सकती हैं।

इस तरह नहीं सभी फाइलों को फ़ोल्डर से प्राप्त किए गए हैं, अगर प्रगणक कम से कम 1 फ़ाइल है फ़ोल्डर

9

में किसी भी फाइल निर्देशिका या उप निर्देशिका के अंदर मौजूद है, तो जाँच करने के लिए, रिक्त नहीं है। नेट 4, आप नीचे विधि का उपयोग कर सकते हैं:

public bool isDirectoryContainFiles(string path) { 
    if (!Directory.Exists(path)) return false; 
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any(); 
} 
संबंधित मुद्दे