2009-07-07 23 views
5

Xem dữ liệu đầu vào và đầu ra được cập nhật tại Chỉnh sửa-1.Tôi làm cách nào để phân tích cú pháp văn bản đã đánh dấu để xử lý thêm?

Những gì tôi đang cố gắng để thực hiện được chuyển

 
+ 1 
+ 1.1 
    + 1.1.1 
    - 1.1.1.1 
    - 1.1.1.2 
+ 1.2 
    - 1.2.1 
    - 1.2.2 
- 1.3 
+ 2 
- 3 

thành một cấu trúc dữ liệu python như

[{'1': [{'1.1': {'1.1.1': ['1.1.1.1', '1.1.1.2']}, '1.2': ['1.2.1', '1.2.2']}, '1.3'], '2': {}}, ['3',]] 

Tôi đã nhìn vào nhiều ngôn ngữ đánh dấu wiki khác nhau, markdown, văn bản cơ cấu lại, nhưng tất cả chúng đều cực kỳ phức tạp đối với tôi để hiểu cách nó hoạt động vì chúng phải bao gồm một số lượng lớn các thẻ và cú pháp (tôi sẽ chỉ cần danh sách "phần" của hầu hết chúng nhưng chuyển thành python thay vì html của khóa học.)

Tôi cũng đã xem xét mã thông báo, từ khóa và trình phân tích cú pháp nhưng lại phức tạp hơn nhiều so với nhu cầu của tôi và tôi có thể hiểu được.

Tôi không biết bắt đầu từ đâu và sẽ đánh giá cao bất kỳ sự trợ giúp nào có thể về chủ đề này. Cảm ơn

Sửa-1: Có nhân vật ở phần đầu của những vấn đề dòng, từ sản lượng yêu cầu từ trước và bây giờ nó có thể được nhìn thấy rằng * biểu thị một nút gốc với trẻ em, các + có con và - không có con (gốc hoặc cách khác) và chỉ là thông tin bổ sung liên quan đến nút đó. Các * là không quan trọng và có thể được trao đổi với + (tôi có thể nhận tình trạng gốc cách khác.)

Do đó yêu cầu mới sẽ được sử dụng chỉ * để biểu thị một nút có hoặc không có trẻ em và - không thể có con. Tôi cũng đã thay đổi nó để khóa không phải là văn bản sau * vì điều đó sẽ không có nghi ngờ thay đổi sau đó thành tiêu đề thực tế.

Ví dụ

 
* 1 
* 1.1 
* 1.2 
    - Note for 1.2 
* 2 
* 3 
- Note for root 

sẽ cung cấp cho

[{'title': '1', 'children': [{'title': '1.1', 'children': []}, {'title': '1.2', 'children': []}]}, {'title': '2', 'children': [], 'notes': ['Note for 1.2', ]}, {'title': '3', 'children': []}, 'Note for root'] 

Hoặc nếu bạn có ý tưởng khác để đại diện cho phác thảo trong python sau đó mang nó về phía trước.

+0

Done và thực hiện. Tôi đã chỉnh sửa cả hai thứ đó. – Rigsby

Trả lời

6

Chỉnh sửa: nhờ làm rõ và thay đổi thông số kỹ thuật Tôi đã chỉnh sửa mã của mình, vẫn sử dụng lớp Node rõ ràng là bước trung gian để rõ ràng - logic là chuyển danh sách các dòng thành danh sách của các nút, sau đó chuyển danh sách các nút đó thành cây (bằng cách sử dụng thuộc tính thụt lề của chúng một cách thích hợp), sau đó in cây đó ở dạng có thể đọc được (đây chỉ là bước "gỡ lỗi", để kiểm tra cây cũng được xây dựng, và Tất nhiên có thể nhận xét trong phiên bản cuối cùng của kịch bản - điều này cũng giống như tất nhiên, sẽ lấy các dòng từ một tệp thay vì phải mã hóa chúng để gỡ lỗi!), cuối cùng xây dựng cấu trúc Python mong muốn và in nó. Dưới đây là đoạn code, và như chúng ta sẽ thấy sau đó kết quả là gần như như OP chỉ với một ngoại lệ - nhưng, các mã đầu tiên:

import sys 

class Node(object): 
    def __init__(self, title, indent): 
    self.title = title 
    self.indent = indent 
    self.children = [] 
    self.notes = [] 
    self.parent = None 
    def __repr__(self): 
    return 'Node(%s, %s, %r, %s)' % (
     self.indent, self.parent, self.title, self.notes) 
    def aspython(self): 
    result = dict(title=self.title, children=topython(self.children)) 
    if self.notes: 
     result['notes'] = self.notes 
    return result 

def print_tree(node): 
    print ' ' * node.indent, node.title 
    for subnode in node.children: 
    print_tree(subnode) 
    for note in node.notes: 
    print ' ' * node.indent, 'Note:', note 

def topython(nodelist): 
    return [node.aspython() for node in nodelist] 

def lines_to_tree(lines): 
    nodes = [] 
    for line in lines: 
    indent = len(line) - len(line.lstrip()) 
    marker, body = line.strip().split(None, 1) 
    if marker == '*': 
     nodes.append(Node(body, indent)) 
    elif marker == '-': 
     nodes[-1].notes.append(body) 
    else: 
     print>>sys.stderr, "Invalid marker %r" % marker 

    tree = Node('', -1) 
    curr = tree 
    for node in nodes: 
    while node.indent <= curr.indent: 
     curr = curr.parent 
    node.parent = curr 
    curr.children.append(node) 
    curr = node 

    return tree 


data = """\ 
* 1 
* 1.1 
* 1.2 
    - Note for 1.2 
* 2 
* 3 
- Note for root 
""".splitlines() 

def main(): 
    tree = lines_to_tree(data) 
    print_tree(tree) 
    print 
    alist = topython(tree.children) 
    print alist 

if __name__ == '__main__': 
    main() 

Khi chạy, điều này phát ra:

1 
    1.1 
    1.2 
    Note: 1.2 
2 
3 
Note: 3 

[{'children': [{'children': [], 'title': '1.1'}, {'notes': ['Note for 1.2'], 'children': [], 'title': '1.2'}], 'title': '1'}, {'children': [], 'title': '2'}, {'notes': ['Note for root'], 'children': [], 'title': '3'}] 

Ngoài các Trật tự của các phím (đó là không đáng kể và không được bảo đảm trong một dict, tất nhiên), đây là gần theo yêu cầu - ngoại trừ việc đây tất cả ghi chú xuất hiện mục như dict với khóa notes và một giá trị danh sách các chuỗi (nhưng mục ghi chú bị bỏ qua nếu danh sách sẽ trống, gần như được thực hiện trong ví dụ trong câu hỏi).

Trong phiên bản hiện tại của câu hỏi, cách thể hiện ghi chú hơi không rõ ràng; một ghi chú xuất hiện dưới dạng một chuỗi độc lập, một số khác là các mục có giá trị là một chuỗi (thay vì danh sách các chuỗi như tôi đang sử dụng). Nó không rõ ràng những gì ngụ ý rằng các lưu ý phải xuất hiện như là một chuỗi độc lập trong một trường hợp và như một mục nhập dict trong tất cả những người khác, vì vậy chương trình này tôi đang sử dụng là thường xuyên hơn; và nếu một lưu ý (nếu có) là một chuỗi đơn chứ không phải là một danh sách, điều đó có nghĩa đó là lỗi nếu có nhiều hơn một nốt xuất hiện cho một nút? Về vấn đề thứ hai, lược đồ này tôi đang sử dụng là tổng quát hơn (cho phép một nút có số lượng ghi chú từ 0 lên, thay vì chỉ 0 hoặc 1 dường như ngụ ý trong câu hỏi).

Đã viết quá nhiều mã (câu trả lời trước khi chỉnh sửa dài và giúp làm rõ và thay đổi thông số kỹ thuật) để cung cấp (tôi hy vọng) 99% giải pháp mong muốn, tôi hy vọng điều này sẽ phù hợp với áp phích gốc, vì vài chỉnh sửa cuối cùng đối với mã và/hoặc thông số kỹ thuật để làm cho chúng phù hợp với nhau nên dễ dàng cho anh ta làm!

+0

Tôi đã cập nhật bài đăng của mình để thử và làm rõ mọi thứ.Bây giờ tôi cho thấy rằng * hoặc - vật chất và tôi đã cố định đầu ra đầu tiên ({'1.2.3'} nên chỉ là một chuỗi và không phải là một dict như tôi đã có.) – Rigsby

1

Vì bạn đang xử lý tình huống phác thảo, bạn có thể đơn giản hóa mọi thứ bằng cách sử dụng ngăn xếp. Về cơ bản, bạn muốn tạo một chồng có dict s tương ứng với độ sâu của đường viền. Khi bạn phân tích cú pháp một dòng mới và độ sâu của đường viền đã tăng lên, bạn hãy đẩy dict mới vào ngăn xếp được tham chiếu trước dict ở đầu ngăn xếp. Khi bạn phân tích cú pháp một dòng có độ sâu thấp hơn, bạn bật ngăn xếp để quay lại phụ huynh. Và khi bạn gặp phải một dòng có cùng chiều sâu, bạn thêm nó vào dict ở đầu ngăn xếp.

+0

Và để có được thực sự ưa thích, bạn có thể sử dụng các nội dung của các mục và re.match để đảm bảo mục tiếp theo bắt đầu với nó cộng với một dấu chấm cộng với số (s). – Kurt

1

Ngăn xếp là một cơ sở hạ tầng thực sự hữu ích khi phân tích cây. Bạn chỉ cần giữ đường dẫn từ nút được thêm cuối cùng lên thư mục gốc trên ngăn xếp mọi lúc, do đó bạn có thể tìm được cha mẹ chính xác theo độ dài của thụt lề. Một cái gì đó như thế này nên làm việc cho phân tích ví dụ cuối cùng của bạn:

import re 
line_tokens = re.compile('(*)(\\*|-) (.*)') 

def parse_tree(data): 
    stack = [{'title': 'Root node', 'children': []}] 
    for line in data.split("\n"): 
     indent, symbol, content = line_tokens.match(line).groups()   
     while len(indent) + 1 < len(stack): 
      stack.pop() # Remove everything up to current parent 
     if symbol == '-': 
      stack[-1].setdefault('notes', []).append(content) 
     elif symbol == '*': 
      node = {'title': content, 'children': []} 
      stack[-1]['children'].append(node) 
      stack.append(node) # Add as the current deepest node 
    return stack[0] 
0

Cú pháp bạn `lại sử dụng là rất tương tự như YAML. Nó có một số khác biệt, nhưng nó khá dễ học - đó là trọng tâm chính là con người có thể đọc được (và có thể ghi).

Hãy xem trang web của Yaml. Có một số ràng buộc python, tài liệu và các công cụ khác ở đó.

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