2012-01-13 43 views
5

Tôi có một danh sách các đường dẫn mà tôi cần kiểm tra các tệp bên trong. Tất nhiên, nếu tôi được đưa ra một thư mục gốc, và một thư mục con, không cần phải xử lý thư mục con. Ví dụ:Cách xác định đường dẫn có phải là thư mục phụ của thư mục khác không?

c:\test // process this 
c:\test\pics // do not process this 
c:\test2 // process this 

Làm cách nào tôi có thể biết (nền tảng chéo) rằng đường dẫn không phải là thư mục phụ của người khác. Tốt hơn tôi muốn điều này là nền tảng chéo, và không lo lắng về các liên kết tượng trưng miễn là chúng không có chu kỳ (trường hợp xấu hơn là tôi sẽ xử lý dữ liệu hai lần).

UPDATE: đây là mã tôi đã kết thúc sử dụng, nhờ @FJ

def unique_path_roots(paths): 
    visited = set() 
    paths = list(set(paths)) 

    for path in sorted(paths,key=cmp_to_key(locale.strcoll)): 
     path = normcase(normpath(realpath(path))) 

     head, tail = os.path.split(path) 
     while head and tail: 
      if head in visited: 
       break 
      head, tail = os.path.split(head) 
     else: 
      yield path 
      visited.add(path) 

Trả lời

6

tôi sẽ duy trì một tập hợp các thư mục bạn đã xử lý, và sau đó cho mỗi lần kiểm tra con đường mới để xem có bất kỳ thư mục mẹ của nó đã tồn tại trong bộ đó trước khi chế biến:

import os.path 

visited = set() 
for path in path_list: 
    head, tail = os.path.split(path) 
    while head and tail: 
     if head in visited: 
      break 
     head, tail = os.path.split(head) 
    else: 
     process(path) 
     visited.add(path) 

Lưu ý rằng path_list nên được sắp xếp sao cho các thư mục con luôn sau khi thư mục mẹ của họ nếu chúng tồn tại.

+0

Đây sẽ là nhanh hơn so với đề nghị của tôi bởi vì nó thiết lập kiểm tra thành viên chứ không phải quét một danh sách. Tôi thích nó. – kindall

+0

@ F.J dường như là một vòng lặp infitie, đầu giảm xuống c: \ tại chính cơ sở của nó và không bao giờ được đặt thành Không. – esac

+0

@esac - Xin lỗi về điều đó, tôi đã nghĩ rằng trong trường hợp cơ sở nó sẽ đặt mọi thứ vào đuôi, không phải đầu. Xem bản chỉnh sửa của tôi để khắc phục sự cố. –

2

Theo dõi các thư mục bạn đã xử lý (ở dạng bình thường) và không xử lý lại chúng nếu bạn đã thấy chúng. Một cái gì đó như thế này nên làm việc:

from os.path import realpath, normcase, sep 

dirs = [r"C:\test", r"C:\test\pics", r"C:\test2"] 

processed = [] 

for dir in dirs: 
    dir = normcase(realpath(dir)) + sep 
    if not any(dir.startswith(p) for p in processed): 
     processed.append(dir) 
     process(dir)   # your code here 
+1

'commonprefix ([r'C: \ test2 ', r'C: \ test']) -> 'C: \\ test'' –

+0

Vâng, thở dài. Điều đó không thực sự làm những gì nó nên, IMHO. Thay đổi nó thành chỉ đơn giản là 'startswith()' - điều đó sẽ ổn vì nó được chuẩn hóa. – kindall

+0

Vâng, tôi thực sự nghĩ rằng hành vi của commonprefix là loại kỳ lạ, nó có vẻ như nó chỉ nên kiểm tra tại thư mục phá vỡ, vì nó đến từ các module 'os.path', oh well. –

8
def is_subdir(path, directory): 
    path = os.path.realpath(path) 
    directory = os.path.realpath(directory) 

    relative = os.path.relpath(path, directory) 

    if relative.startswith(os.pardir): 
     return False 
    else: 
     return True 
+1

os.sep là nguồn lỗi :) –

+0

Sử dụng 'relpath' có thể thất bại trên các cửa sổ ms, khi cố gắng tìm' C: \ foo' liên quan đến 'D: \ bar'. – ideasman42

-1

cố định và đơn giản hóa jgoeders 's phiên bản:

def is_subdir(suspect_child, suspect_parent): 
    suspect_child = os.path.realpath(suspect_child) 
    suspect_parent = os.path.realpath(suspect_parent) 

    relative = os.path.relpath(suspect_child, start=suspect_parent) 

    return not relative.startswith(os.pardir) 
+0

Tại sao không chỉ chỉnh sửa bài đăng thay vì thêm một câu trả lời trùng lặp khác? – jgoeders

+0

Xin lỗi, tôi đăng trên SO một lần trong Mặt trăng xanh và bỏ qua tính năng này. –

+0

'realpath' - loại bỏ các liên kết tượng trưng ở đây là có vấn đề. (trong nhiều trường hợp không phải là những gì bạn muốn), vì các liên kết sau đây có thể thay đổi hoàn toàn bố cục đường dẫn. – ideasman42

0

Dưới đây là một chức năng tiện ích is_subdir tôi đã đưa ra.

  • Python3.x tương thích (chỉ hoạt động với bytesstr, phù hợp với os.path mà còn hỗ trợ cả hai).
  • Bình thường hóa các đường để so sánh.
    (hệ thống cấp bậc gốc và trường hợp hoạt động trên các cửa sổ ms).
  • Tránh sử dụng os.path.relpath sẽ tăng ngoại lệ trên cửa sổ ms nếu đường dẫn trên các ổ đĩa khác nhau. (C:\foo ->D:\bar)

Code:

def is_subdir(path, directory): 
    """ 
    Returns true if *path* in a subdirectory of *directory*. 
    """ 
    import os 
    from os.path import normpath, normcase, sep 
    path = normpath(normcase(path)) 
    directory = normpath(normcase(directory)) 
    if len(path) > len(directory): 
     sep = sep.encode('ascii') if isinstance(directory, bytes) else sep 
     if path.startswith(directory.rstrip(sep) + sep): 
      return True 
    return False 
Các vấn đề liên quan