2010-02-06 137 views
118

我有一個C++/Obj-C背景,我只是發現Python(已經寫了大約一個小時)。 我正在寫一個腳本遞歸讀取文件夾結構中的文本文件的內容。閱讀Python遞歸文件夾

我遇到的問題是我寫的代碼只能用於一個文件夾。我可以看到爲什麼在代碼中(見#hardcoded path),我只是不知道如何繼續使用Python,因爲我的經驗只是全新的。

Python代碼:

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() 

回答

221

確保您瞭解os.walk三個返回值:

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

具有以下含義:

  • root:「走過」的當前路徑
  • subdirs:文件中的目錄類型
  • filesroot:文件中的目錄相比

並請使用os.path.join,而不是用斜槓串聯的其他類型的root(不subdirs)!您的問題是filePath = rootdir + '/' + file - 您必須連接當前「走」的文件夾而不是最頂層的文件夾。所以那一定是filePath = os.path.join(root, file)。順便說一句「文件」是一個內置的,所以你通常不使用它作爲變量名稱。

的另一個問題是你的循環,這應該是這樣的,例如:

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 – 2010-02-06 09:52:41

+8

擡起頭給任何人當啞巴/忘記爲我...這個代碼示例一個txt文件寫入到每個目錄。很高興我測試了它在版本控制的文件夾,但我需要的一切寫一個清理腳本也在這裏:) – Steazy 2014-09-24 23:56:33

0

我認爲這個問題是你沒有正確地處理的os.walk輸出。

首先,改變:

filePath = rootdir + '/' + file 

到:

filePath = root + '/' + file 

rootdir是你的固定起始目錄; root是由os.walk返回的目錄。

其次,你不需要縮進你的文件處理循環,因爲這對每個子目錄都是沒有意義的。你會得到root設置爲每個子目錄。你不需要手工處理子目錄,除非你想對目錄本身做些什麼。

+0

我在每個子目錄數據,所以我需要有一個單獨的文本文件每個目錄的內容。 – 2010-02-06 09:36:59

+0

@Bock:文件部分是當前目錄中的文件列表。所以縮進確實是錯誤的。你正在寫爲'文件路徑= ROOTDIR +「/」 + file',不健全的權利:文件是從當前的文件列表,所以你寫了很多現有的文件? – 2010-02-06 09:52:41

2

使用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() 
+0

它看起來像這個代碼僅適用於文件夾2級(或更深)。它仍然讓我更接近。 – 2010-02-06 09:48:27

23

同意與Dave Webb,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()) 
+1

尼斯之一。這也適用。不過我更喜歡AndiDog的版本,儘管它的時間更長,因爲它更清楚地理解爲Python的初學者。 +1 – 2010-02-06 10:08:40

0

os.walk默認完成遞歸的步行路程。對於每一個目錄,從根開始它產生一個3元組(dirpath,dirnames中,文件名)

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

在Python 2.6'步行()'** **做遞歸返回列表。我想你的代碼,並得到了一個清單,許多重複的......如果你只是刪除註釋「子文件夾#遞歸調用」下的線 - 它工作得很好 – borisbn 2012-09-28 05:20:23

+0

@borisbn你是對的,THX! – b1r3k 2013-05-23 12:20:05

0

試試這個:

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 
13

如果您正在使用Python 3.5+或以上,就可以得到這在1行完成。

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

如前所述in documentation

If recursive is true, the pattern '**' will match any files and zero or more directories and subdirectories.

如果你想每一個文件,你可以使用

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

類型錯誤:iglob()得到了一個意想不到的關鍵字參數「遞歸」 – Jewenile 2017-09-01 07:19:10

+0

正如開頭提到的,它只是個面向Python 3.5+ – ChillarAnand 2017-09-01 09:43:17

+0

呀,還沒有注意到,而我也已經3.5+,bash的解釋犯規等等。對不起打擾。 – Jewenile 2017-09-02 10:04:55