अजगर पुनरावर्ती फ़ोल्डर पढ़ा


224

मेरे पास C ++ / Obj-C बैकग्राउंड है और मैं सिर्फ पायथन की खोज कर रहा हूं (लगभग एक घंटे से इसे लिख रहा हूं)। मैं एक फ़ोल्डर संरचना में पाठ फ़ाइलों की सामग्री को पुन: पढ़ने के लिए एक स्क्रिप्ट लिख रहा हूं।

मेरे पास जो समस्या है वह कोड है जो मैंने लिखा है वह केवल एक फ़ोल्डर के लिए काम करेगा गहरा। मैं कोड में क्यों देख सकता हूं (देखें #hardcoded path), मुझे नहीं पता कि मैं पायथन के साथ कैसे आगे बढ़ सकता हूं क्योंकि मेरे अनुभव के साथ यह केवल नया है।

पायथन कोड:

import os
import sys

rootdir = sys.argv[1]

for root, subFolders, files in os.walk(rootdir):

    for folder in subFolders:
        outfileName = rootdir + "/" + folder + "/py-outfile.txt" # hardcoded path
        folderOut = open( outfileName, 'w' )
        print "outfileName is " + outfileName

        for file in files:
            filePath = rootdir + '/' + file
            f = open( filePath, 'r' )
            toWrite = f.read()
            print "Writing '" + toWrite + "' to" + filePath
            folderOut.write( toWrite )
            f.close()

        folderOut.close()

जवाबों:


346

सुनिश्चित करें कि आप तीन रिटर्न मानों को समझते हैं os.walk:

for root, subdirs, files in os.walk(rootdir):

निम्नलिखित अर्थ है:

  • root: वर्तमान पथ जो "के माध्यम से चला गया है"
  • subdirs: rootटाइप डायरेक्टरी में फाइलें
  • files: निर्देशिका के अलावा अन्य प्रकार की root(में subdirs) फाइलें

और कृपया os.path.joinएक स्लैश के साथ समवर्ती करने के बजाय उपयोग करें ! आपकी समस्या यह है filePath = rootdir + '/' + file- आपको वर्तमान में "सबसे ज्यादा चलने वाले" फ़ोल्डर को सबसे ऊपरी फ़ोल्डर के बजाय संक्षिप्त करना चाहिए। तो वह होना ही चाहिए filePath = os.path.join(root, file)। BTW "फ़ाइल" एक बिलिन है, इसलिए आप इसे सामान्य रूप से चर नाम के रूप में उपयोग नहीं करते हैं।

एक अन्य समस्या आपके छोरों की है, जो इस तरह होना चाहिए, उदाहरण के लिए:

import os
import sys

walk_dir = sys.argv[1]

print('walk_dir = ' + walk_dir)

# If your current working directory may change during script execution, it's recommended to
# immediately convert program arguments to an absolute path. Then the variable root below will
# be an absolute path as well. Example:
# walk_dir = os.path.abspath(walk_dir)
print('walk_dir (absolute) = ' + os.path.abspath(walk_dir))

for root, subdirs, files in os.walk(walk_dir):
    print('--\nroot = ' + root)
    list_file_path = os.path.join(root, 'my-directory-list.txt')
    print('list_file_path = ' + list_file_path)

    with open(list_file_path, 'wb') as list_file:
        for subdir in subdirs:
            print('\t- subdirectory ' + subdir)

        for filename in files:
            file_path = os.path.join(root, filename)

            print('\t- file %s (full path: %s)' % (filename, file_path))

            with open(file_path, 'rb') as f:
                f_content = f.read()
                list_file.write(('The file %s contains:\n' % filename).encode('utf-8'))
                list_file.write(f_content)
                list_file.write(b'\n')

यदि आप नहीं जानते हैं, withफाइलों के लिए बयान एक आशुलिपि है:

with open('filename', 'rb') as f:
    dosomething()

# is effectively the same as

f = open('filename', 'rb')
try:
    dosomething()
finally:
    f.close()

4
शानदार, बहुत सारे प्रिंट समझने के लिए कि क्या चल रहा है और यह पूरी तरह से काम करता है। धन्यवाद! +1
ब्रॉक वुल्फ

16
मेरे जैसे किसी भी गूंगे / अनजान व्यक्ति के लिए सिर ... यह कोड नमूना प्रत्येक निर्देशिका के लिए एक txt फ़ाइल लिखता है। खुशी है कि मैंने इसे एक संस्करण नियंत्रित फ़ोल्डर में परीक्षण किया, हालांकि मुझे एक सफाई स्क्रिप्ट लिखने की आवश्यकता है सब कुछ यहां भी है :)
स्टीज़े

उस दूसरे (सबसे लंबे समय तक) कोड स्निपेट ने बहुत अच्छा काम किया, मुझे बहुत उबाऊ काम से बचाया
उभयचर

1
चूंकि गति अगर स्पष्ट रूप से सबसे महत्वपूर्ण पहलू है, os.walkतो बुरा नहीं है, हालांकि मैं इसके माध्यम से और भी तेज तरीके से आया हूं os.scandir। सभी globसमाधान की तुलना में बहुत कम होती है walkऔर scandir। मेरा कार्य, साथ ही एक पूर्ण गति विश्लेषण, यहां पाया जा सकता है: stackoverflow.com/a/59803793/2441026
user136036

112

यदि आप पायथन 3.5 या इसके बाद के संस्करण का उपयोग कर रहे हैं, तो आप इसे 1 पंक्ति में प्राप्त कर सकते हैं।

import glob

for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
     print(filename)

जैसा कि प्रलेखन में उल्लेख किया गया है

यदि पुनरावर्ती सत्य है, तो पैटर्न '**' किसी भी फाइल और शून्य या अधिक निर्देशिकाओं और उपनिर्देशिकाओं से मेल खाएगा।

यदि आप प्रत्येक फ़ाइल चाहते हैं, तो आप उपयोग कर सकते हैं

import glob

for filename in glob.iglob(root_dir + '**/*', recursive=True):
     print(filename)

TypeError: iglob () को एक अप्रत्याशित कीवर्ड तर्क 'पुनरावर्ती' मिला
ज्वैनील

1
जैसा कि शुरुआत में बताया गया है, यह केवल पायथन 3.5+ के लिए है
चिल्लरअनंद

9
root_dir के पास एक अनुगामी स्लैश होना चाहिए (अन्यथा आपको पहले तर्क के रूप में 'फ़ोल्डर / ** / *' के बजाय 'फ़ोल्डर ** / *' जैसा कुछ मिलता है)। आप os.path.join (root_dir, ' * / ') का उपयोग कर सकते हैं , लेकिन मुझे नहीं पता कि वाइल्डकार्ड रास्तों के साथ os.path.join का उपयोग करना स्वीकार्य है (यह मेरे आवेदन के लिए काम करता है)।
drojf

@ChillarAnand क्या आप इस उत्तर में कोड के लिए एक टिप्पणी जोड़ सकते हैं जिसमें root_dirएक अनुगामी स्लैश की आवश्यकता है? इससे लोगों का समय बचेगा (या कम से कम इससे मेरा समय बचा होगा)। धन्यवाद।
दान निसानबाम

1
अगर मैं इस जवाब के रूप में यह दौड़ाया यह पुनरावृत्ति काम नहीं किया। इस काम रिकर्सिवली मैं करने के लिए इसे बदलना पड़ा करने के लिए: glob.iglob(root_dir + '**/**', recursive=True)। मैं अजगर 3.8.2 में काम कर रहा हूँ
मिकी

38

डेव वेब के साथ सहमत, os.walkपेड़ में प्रत्येक निर्देशिका के लिए एक आइटम का उत्पादन करेगा। तथ्य यह है, आपको सिर्फ ध्यान रखने की जरूरत नहीं है subFolders

इस तरह कोड काम करना चाहिए:

import os
import sys

rootdir = sys.argv[1]

for folder, subs, files in os.walk(rootdir):
    with open(os.path.join(folder, 'python-outfile.txt'), 'w') as dest:
        for filename in files:
            with open(os.path.join(folder, filename), 'r') as src:
                dest.write(src.read())

3
अच्छा है। यह भी काम करता है। मैं हालांकि एंडीडॉग के संस्करण को पसंद करता हूं, भले ही यह अधिक लंबा हो क्योंकि यह पायथन के लिए एक शुरुआत के रूप में समझने के लिए स्पष्ट है। +1
ब्रॉक वुल्फ

20

टीएल; डीआर: यह find -type fनीचे और वर्तमान सहित सभी फ़ोल्डरों में सभी फाइलों पर जाने के बराबर है :

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))

जैसा कि पहले से ही अन्य उत्तरों में बताया गया है, os.walk()यह उत्तर है, लेकिन इसे बेहतर तरीके से समझाया जा सकता है। यह काफी सरल है! चलो इस पेड़ के माध्यम से चलते हैं:

docs/
└── doc1.odt
pics/
todo.txt

इस कोड के साथ:

for currentpath, folders, files in os.walk('.'):
    print(currentpath)

currentpathवर्तमान फ़ोल्डर इसे देख रहा है। यह आउटपुट होगा:

.
./docs
./pics

तो यह तीन बार लूप करता है, क्योंकि तीन फ़ोल्डर हैं: वर्तमान एक docs, और pics। प्रत्येक लूप में, यह चर foldersऔर filesसभी फ़ोल्डर और फ़ाइलों के साथ भरता है । आइए दिखाते हैं उन्हें:

for currentpath, folders, files in os.walk('.'):
    print(currentpath, folders, files)

यह हमें दिखाता है:

# currentpath  folders           files
.              ['pics', 'docs']  ['todo.txt']
./pics         []                []
./docs         []                ['doc1.odt']

तो पहली पंक्ति में, हम देखते हैं कि हम फ़ोल्डर में हैं ., कि इसमें दो फ़ोल्डर हैं अर्थात् picsऔर docs, और एक फ़ाइल है, अर्थात् todo.txt। आपको उन फ़ोल्डरों में पुनरावृत्ति करने के लिए कुछ भी करने की ज़रूरत नहीं है, क्योंकि जैसा कि आप देखते हैं, यह स्वचालित रूप से पुनरावृत्ति करता है और बस आपको किसी भी सबफ़ोल्डर में फाइलें देता है। और उस के किसी भी सबफ़ोल्डर (हालांकि हमारे पास उदाहरण में नहीं हैं)।

यदि आप सभी फ़ाइलों के माध्यम से लूप करना चाहते हैं find -type f, तो आप यह कर सकते हैं:

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))

यह आउटपुट:

./todo.txt
./docs/doc1.odt

9

pathlibपुस्तकालय फाइलों के साथ काम करने के लिए वास्तव में बहुत अच्छा है। आप किसी Pathऑब्जेक्ट पर पुनरावर्ती ग्लोब कर सकते हैं जैसे।

from pathlib import Path

for elem in Path('/path/to/my/files').rglob('*.*'):
    print(elem)

6

यदि आप किसी दिए गए डायर ( find .शेल में) के तहत सभी रास्तों की एक फ्लैट सूची चाहते हैं :

   files = [ 
       os.path.join(parent, name)
       for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
       for name in files + subdirs
   ]

केवल बेस डायर के तहत फाइलों में पूर्ण पथ शामिल करने के लिए, बाहर निकलें + subdirs


6
import glob
import os

root_dir = <root_dir_here>

for filename in glob.iglob(root_dir + '**/**', recursive=True):
    if os.path.isfile(filename):
        with open(filename,'r') as file:
            print(file.read())

**/**सहित सभी फ़ाइलों को पुन: प्राप्त करने के लिए उपयोग किया जाता है directory

if os.path.isfile(filename)यह जाँचने के लिए प्रयोग किया जाता है कि filenameचर क्या है fileया directoryयदि यह फ़ाइल है तो हम उस फ़ाइल को पढ़ सकते हैं। यहां मैं फाइल प्रिंट कर रहा हूं।


6

मैंने निम्नलिखित को सबसे आसान पाया है

from glob import glob
import os

files = [f for f in glob('rootdir/**', recursive=True) if os.path.isfile(f)]

उपयोग करने glob('some/path/**', recursive=True)से सभी फाइलें मिलती हैं, लेकिन इसमें निर्देशिका नाम भी शामिल हैं। if os.path.isfile(f)शर्त जोड़कर इस सूची को मौजूदा फाइलों में ही फिल्टर कर देता है


3

os.path.join()अपने रास्तों का निर्माण करने के लिए उपयोग करें - यह शून्य है:

import os
import sys
rootdir = sys.argv[1]
for root, subFolders, files in os.walk(rootdir):
    for folder in subFolders:
        outfileName = os.path.join(root,folder,"py-outfile.txt")
        folderOut = open( outfileName, 'w' )
        print "outfileName is " + outfileName
        for file in files:
            filePath = os.path.join(root,file)
            toWrite = open( filePath).read()
            print "Writing '" + toWrite + "' to" + filePath
            folderOut.write( toWrite )
        folderOut.close()

ऐसा लगता है कि यह कोड केवल 2 स्तरों (या अधिक गहराई) के लिए काम करता है। फिर भी यह मुझे करीब लाता है।
ब्रॉक वुल्फ

1

os.walkडिफ़ॉल्ट रूप से पुनरावर्ती चलता है। प्रत्येक dir के लिए, मूल से शुरू होकर यह 3-tuple (dirpath, dirnames, filenames) पैदा करता है

from os import walk
from os.path import splitext, join

def select_files(root, files):
    """
    simple logic here to filter out interesting files
    .py files in this example
    """

    selected_files = []

    for file in files:
        #do concatenation here to get full path 
        full_path = join(root, file)
        ext = splitext(file)[1]

        if ext == ".py":
            selected_files.append(full_path)

    return selected_files

def build_recursive_dir_tree(path):
    """
    path    -    where to begin folder scan
    """
    selected_files = []

    for root, dirs, files in walk(path):
        selected_files += select_files(root, files)

    return selected_files

1
अजगर 2.6 में walk() करते पुनरावर्ती सूची वापस जाएँ। मैंने आपका कोड आज़माया और कई रिपीट के साथ एक सूची प्राप्त की ... यदि आप टिप्पणी "# सबफ़ोल्डर्स पर पुनरावर्ती कॉल" के तहत लाइनें हटाते हैं - यह ठीक काम करता है
बोरिसबैन

1

इसे इस्तेमाल करे:

import os
import sys

for root, subdirs, files in os.walk(path):

    for file in os.listdir(root):

        filePath = os.path.join(root, file)

        if os.path.isdir(filePath):
            pass

        else:
            f = open (filePath, 'r')
            # Do Stuff

जब आप पहले से ही निर्देशिका लिस्टिंग को फाइलों में विभाजित कर चुके हैं और वॉक () से निर्देशिकाओं को विभाजित कर रहे हैं, तो आप एक और सूची () और फिर आईएसडीआर () क्यों करेंगे? ऐसा लगता है कि यह बड़े पेड़ों में धीमी गति से होगा (एक के बजाय तीन syscalls करें: 1 = चलना, 2 = सूची, 3 = isdir, बजाय केवल चलना और 'उप-मंडल' और 'फ़ाइलों' के माध्यम से लूप)।
ल्यूक

0

मुझे लगता है कि समस्या यह है कि आप os.walkसही तरीके से आउटपुट का प्रसंस्करण नहीं कर रहे हैं ।

सबसे पहले, बदलें:

filePath = rootdir + '/' + file

सेवा:

filePath = root + '/' + file

rootdirआपकी निश्चित शुरुआत निर्देशिका है; rootएक निर्देशिका द्वारा दिया है os.walk

दूसरे, आपको अपने फाइल प्रोसेसिंग लूप को इंडेंट करने की आवश्यकता नहीं है, क्योंकि यह प्रत्येक उपनिर्देशिका के लिए इसे चलाने का कोई मतलब नहीं है। आप rootप्रत्येक उपनिर्देशिका पर सेट हो जाएंगे । जब तक आप स्वयं निर्देशिकाओं के साथ कुछ करना चाहते हैं, तब तक आपको उपनिर्देशिका को हाथ से संसाधित करने की आवश्यकता नहीं है।


मेरे पास प्रत्येक उप निर्देशिका में डेटा है, इसलिए मुझे प्रत्येक निर्देशिका की सामग्री के लिए एक अलग पाठ फ़ाइल की आवश्यकता है।
ब्रॉक वुल्फ

@Brock: फ़ाइलें हिस्सा वर्तमान निर्देशिका में फ़ाइलों की सूची है। तो वास्तव में इंडेंटेशन गलत है। आप लिख रहे हैं filePath = rootdir + '/' + file, यह सही नहीं लगता है: फ़ाइल वर्तमान फ़ाइलों की सूची से है, इसलिए आप बहुत सारी मौजूदा फ़ाइलों को लिख रहे हैं?
आलोक सिंघल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.