2010-02-06 72 views
118

Tôi có nền C++/Obj-C và tôi chỉ khám phá ra Python (đã viết nó trong khoảng một giờ). Tôi đang viết một tập lệnh để đệ quy đọc nội dung của tệp văn bản trong cấu trúc thư mục.Thư mục đệ quy Python đọc

Sự cố tôi có là mã tôi đã viết sẽ chỉ hoạt động cho một thư mục sâu. Tôi có thể thấy lý do tại sao trong mã (xem #hardcoded path), tôi chỉ không biết làm thế nào tôi có thể di chuyển về phía trước với Python kể từ khi kinh nghiệm của tôi với nó chỉ là thương hiệu mới.

Python Mã số:

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

Trả lời

221

Hãy chắc chắn rằng bạn hiểu được ba giá trị trở lại của os.walk:

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

có ý nghĩa như sau:

  • root: Đường dẫn hiện tại "đi qua"
  • subdirs: Các file trong đề root kiểu thư mục
  • files: Các file trong đề root (không phải trong subdirs) loại trừ thư mục

Và xin vui lòng sử dụng os.path.join thay vì concatenating với một dấu gạch chéo! Vấn đề của bạn là filePath = rootdir + '/' + file - bạn phải ghép nối thư mục "đi bộ" hiện tại thay vì thư mục trên cùng. Vì vậy, phải là filePath = os.path.join(root, file). BTW "tập tin" là một builtin, vì vậy bạn không thường sử dụng nó như là tên biến.

Một vấn đề khác là các vòng của bạn, mà nên như thế này, ví dụ:

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

Nếu bạn không biết, báo cáo kết quả with cho các tập tin là một cách viết tắt:

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

# is effectively the same as 

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

Tuyệt vời, rất nhiều bản in để hiểu những gì đang xảy ra và nó hoạt động hoàn hảo. Cảm ơn! +1 –

+8

Theo dõi bất kỳ ai câm/lãng quên như tôi ... mẫu mã này viết một tệp txt vào mỗi thư mục. Vui vì tôi đã thử nghiệm nó trong một thư mục kiểm soát phiên bản, mặc dù tất cả mọi thứ tôi cần để viết một kịch bản dọn dẹp cũng ở đây :) – Steazy

0

Tôi nghĩ vấn đề là bạn không xử lý đầu ra của os.walk một cách chính xác.

Thứ nhất, sự thay đổi:

filePath = rootdir + '/' + file 

tới:

filePath = root + '/' + file 

rootdir là thư mục bắt đầu cố định của bạn; root là một thư mục được trả về bởi os.walk.

Thứ hai, bạn không cần thụt lề vòng xử lý tệp vì không có ý nghĩa gì khi chạy thao tác này cho từng thư mục con. Bạn sẽ nhận được root được đặt cho từng thư mục con. Bạn không cần phải xử lý các thư mục con bằng tay trừ khi bạn muốn tự mình làm điều gì đó với các thư mục.

+0

Tôi có dữ liệu trong mỗi thư mục phụ, vì vậy tôi cần phải có một file văn bản riêng biệt cho nội dung của mỗi thư mục. –

+0

@Brock: phần tệp là danh sách tệp trong thư mục hiện tại. Vì vậy, thụt đầu dòng thực sự là sai. Bạn đang viết thư cho 'filePath = rootdir + '/' + file', điều đó không đúng: tập tin nằm trong danh sách các tập tin hiện tại, vì vậy bạn đang ghi vào nhiều tệp hiện có? –

2

sử dụng os.path.join() để xây dựng con đường của bạn - Đó là neater:

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

Dường như mã này hoạt động cho các thư mục 2 cấp (hoặc sâu hơn). Nó vẫn khiến tôi gần hơn. –

23

Đồng ý với Dave Webb, os.walk sẽ mang lại một mục cho mỗi thư mục trong cây. Sự thật là, bạn chỉ cần không quan tâm đến subFolders.

Mã như thế này nên làm việc:

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

Đẹp nhất. Điều này cũng hoạt động. Tuy nhiên, tôi thích phiên bản AndiDog mặc dù nó dài hơn bởi vì nó rõ ràng hơn để hiểu như là một người mới bắt đầu với Python. +1 –

0

os.walk không đi bộ đệ quy theo mặc định. Đối với mỗi dir, bắt đầu từ gốc nó mang lại một 3-tuple (dirpath, dirnames, tên tập tin)

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

Trong Python 2.6 'walk()' ** do ** trả về danh sách đệ quy. Tôi đã thử mã của bạn và nhận được một danh sách với nhiều lần lặp lại ... Nếu bạn chỉ cần loại bỏ dòng dưới bình luận "# đệ quy cuộc gọi trên thư mục con" - nó hoạt động tốt – borisbn

+0

@borisbn bạn là đúng, thx! – b1r3k

0

Hãy thử điều này:

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

Nếu bạn đang sử dụng Python 3.5+ trở lên, bạn có thể nhận được điều này được thực hiện trong 1 dòng.

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

Như đã đề cập in documentation

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

Nếu bạn muốn mọi tập tin, bạn có thể sử dụng

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

TypeError: iglob() có một đối số từ khóa không mong muốn 'đệ quy' – Jewenile

+0

Như đã đề cập ở phần đầu, nó chỉ dành cho Python 3.5+ – ChillarAnand

+0

Yeah, havent nhận thấy rằng trong khi tôi có 3.5+, trình thông dịch bash không như vậy. Xin lỗi vì làm phiền. – Jewenile

Các vấn đề liên quan