2017-08-30 17 views
5

Tôi có một cây kế toán được lưu trữ với indents/không gian trong nguồn:Làm thế nào để phân tích hệ thống phân cấp dựa trên indents với python

Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 

Có một số cố định của các cấp, vì vậy tôi muốn san bằng hệ thống phân cấp bằng cách sử dụng 3 lĩnh vực (số liệu thực tế có 6 cấp, đơn giản hóa ví dụ):

L1  L2   L3 
Income 
Income Revenue 
Income Revenue  IAP 
Income Revenue  Ads 
Income Other-Income 
Expenses Developers In-house 
... etc 

tôi có thể làm điều này bằng cách kiểm tra số lượng chỗ trước tên tài khoản:

for rownum in range(6,ws.max_row+1): 
    accountName = str(ws.cell(row=rownum,column=1).value) 
    indent = len(accountName) - len(accountName.lstrip(' ')) 
    if indent == 0: 
     l1 = accountName 
     l2 = '' 
     l3 = '' 
    elif indent == 3: 
     l2 = accountName 
     l3 = '' 
    else: 
     l3 = accountName 

    w.writerow([l1,l2,l3]) 

Có cách nào linh hoạt hơn để đạt được điều này dựa trên sự thụt dòng của hàng hiện tại so với hàng trước thay vì giả sử nó luôn luôn là 3 khoảng trống cho mỗi cấp? L1 sẽ không có thụt lề, và chúng ta có thể tin tưởng rằng các mức thấp hơn sẽ được thụt lề xa hơn cha mẹ của chúng, nhưng có lẽ không phải lúc nào cũng có 3 khoảng trống cho mỗi cấp độ.

Cập nhật, kết thúc với điều này là thịt logic, vì cuối cùng tôi muốn danh sách tài khoản có nội dung, có vẻ dễ nhất chỉ cần sử dụng thụt lề để quyết định có đặt lại, nối hoặc pop danh sách:

 if indent == 0: 
      accountList = [] 
      accountList.append((indent,accountName)) 
     elif indent > prev_indent: 
      accountList.append((indent,accountName)) 
     elif indent <= prev_indent: 
      max_indent = int(max(accountList,key=itemgetter(0))[0]) 
      while max_indent >= indent: 
       accountList.pop() 
       max_indent = int(max(accountList,key=itemgetter(0))[0]) 
      accountList.append((indent,accountName)) 

Vì vậy, tại mỗi hàng đầu ra, danh sách tài khoản hoàn tất.

Trả lời

4

Bạn có thể bắt chước cách Python phân tích cú pháp thụt đầu dòng. Đầu tiên, tạo một ngăn xếp chứa các mức thụt đầu dòng. Tại mỗi dòng:

  • Nếu thụt đầu dòng lớn hơn chồng, đẩy nó và tăng độ sâu.
  • Nếu nó giống nhau, hãy tiếp tục ở cùng cấp.
  • Nếu nó thấp hơn, hãy bật đầu ngăn xếp trong khi nó cao hơn dấu đầu dòng mới. Nếu bạn tìm thấy mức thụt lề thấp hơn trước khi tìm chính xác như cũ, thì có một lỗi thụt đầu dòng.
indentation = [] 
indentation.append(0) 
depth = 0 

f = open("test.txt", 'r') 

for line in f: 
    line = line[:-1] 

    content = line.strip() 
    indent = len(line) - len(content) 
    if indent > indentation[-1]: 
     depth += 1 
     indentation.append(indent) 

    elif indent < indentation[-1]: 
     while indent < indentation[-1]: 
      depth -= 1 
      indentation.pop() 

     if indent != indentation[-1]: 
      raise RuntimeError("Bad formatting") 

    print(f"{content} (depth: {depth})") 

Với một file "test.txt" có nội dung như bạn cung cấp:

Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 

Đây là kết quả:

Income (depth: 0) 
Revenue (depth: 1) 
IAP (depth: 2) 
Ads (depth: 2) 
Other-Income (depth: 1) 
Expenses (depth: 0) 
Developers (depth: 1) 
In-house (depth: 2) 
Contractors (depth: 2) 
Advertising (depth: 1) 
Other Expense (depth: 1) 

Vì vậy, những gì có thể bạn làm gì với điều này? Giả sử bạn muốn tạo danh sách lồng nhau. Đầu tiên, tạo một ngăn xếp dữ liệu.

  • Khi bạn tìm thấy thụt đầu dòng, hãy thêm một danh sách mới vào cuối ngăn dữ liệu.
  • Khi bạn tìm thấy dấu gạch ngang, hãy mở danh sách trên cùng và thêm nó vào đầu mới.

Và bất kể, đối với mỗi dòng, hãy thêm nội dung vào danh sách ở đầu ngăn xếp dữ liệu.

Đây là việc thực hiện tương ứng:

for line in f: 
    line = line[:-1] 

    content = line.strip() 
    indent = len(line) - len(content) 
    if indent > indentation[-1]: 
     depth += 1 
     indentation.append(indent) 
     data.append([]) 

    elif indent < indentation[-1]: 
     while indent < indentation[-1]: 
      depth -= 1 
      indentation.pop() 
      top = data.pop() 
      data[-1].append(top) 

     if indent != indentation[-1]: 
      raise RuntimeError("Bad formatting") 

    data[-1].append(content) 

while len(data) > 1: 
    top = data.pop() 
    data[-1].append(top) 

danh sách lồng nhau của bạn là ở đầu data chồng của bạn. Kết quả cho cùng một tệp là:

['Income', 
    ['Revenue', 
     ['IAP', 
     'Ads' 
     ], 
    'Other-Income' 
    ], 
'Expenses', 
    ['Developers', 
     ['In-house', 
     'Contractors' 
     ], 
    'Advertising', 
    'Other Expense' 
    ] 
] 

Điều này khá dễ thao tác, mặc dù lồng nhau khá sâu. Bạn có thể truy cập dữ liệu bằng cách kết nối các mục truy cập:

>>> l = data[0] 
>>> l 
['Income', ['Revenue', ['IAP', 'Ads'], 'Other-Income'], 'Expenses', ['Developers', ['In-house', 'Contractors'], 'Advertising', 'Other Expense']] 
>>> l[1] 
['Revenue', ['IAP', 'Ads'], 'Other-Income'] 
>>> l[1][1] 
['IAP', 'Ads'] 
>>> l[1][1][0] 
'IAP' 
+0

Cảm ơn cho điều này, tôi rốt cuộc muốn để có thể sản xuất các hệ thống phân cấp tại mỗi hàng cùng với các nội dung của dòng, vì vậy tôi sửa đổi một chút, nhưng điều này đã cho tôi đi đúng hướng. –

2

Nếu thụt đầu dòng là một số tiền cố định của không gian (3 gian ở đây), bạn có thể đơn giản hóa việc tính toán mức độ thụt đầu dòng.

lưu ý: tôi sử dụng một StringIO để mô phỏng một tập tin

import io 
import itertools 

content = u"""\ 
Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 
""" 

stack = [] 
for line in io.StringIO(content): 
    content = line.rstrip() # drop \n 
    row = content.split(" ") 
    stack[:] = stack[:len(row) - 1] + [row[-1]] 
    print("\t".join(stack)) 

Bạn nhận:

Income 
Income Revenue 
Income Revenue IAP 
Income Revenue Ads 
Income Other-Income 
Expenses 
Expenses Developers 
Expenses Developers In-house 
Expenses Developers Contractors 
Expenses Advertising 
Expenses Other Expenses 

EDIT: thụt đầu dòng không cố định

Nếu thụt đầu dòng không phải là cố định (bạn không phải lúc nào cũng có 3 khoảng trắng) như trong ví dụ bên dưới:

content = u"""\ 
Income 
    Revenue 
    IAP 
    Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 
""" 

Bạn cần phải ước lượng chuyển tại mỗi dòng sản phẩm mới:

stack = [] 
last_indent = u"" 
for line in io.StringIO(content): 
    indent = "".join(itertools.takewhile(lambda c: c == " ", line)) 
    shift = 0 if indent == last_indent else (-1 if len(indent) < len(last_indent) else 1) 
    index = len(stack) + shift 
    stack[:] = stack[:index - 1] + [line.strip()] 
    last_indent = indent 
    print("\t".join(stack)) 
Các vấn đề liên quan