복붙노트

[PYTHON] 파이썬에서 들여 쓰기 된 텍스트 파일에서 나무 / 깊게 중첩 된 dict 만들기

PYTHON

파이썬에서 들여 쓰기 된 텍스트 파일에서 나무 / 깊게 중첩 된 dict 만들기

기본적으로 파일을 반복하고 각 줄의 내용을 깊게 중첩 된 dict에 넣고 싶습니다. 구조는 각 줄의 시작 부분에있는 공백의 크기로 정의됩니다.

근본적으로 목표는 다음과 같은 것을 취하는 것입니다 :

a
    b
        c
    d
        e

그리고 이것을 다음과 같이 바꾸십시오 :

{"a":{"b":"c","d":"e"}}

아니면 이거:

apple
    colours
        red
        yellow
        green
    type
        granny smith
    price
        0.10

이것으로 :

{"apple":{"colours":["red","yellow","green"],"type":"granny smith","price":0.10}

그래서 파이썬의 JSON 모듈로 보내서 JSON을 만들 수 있습니다.

지금 나는 그런 단계에서 dict과리스트를 만들려고 노력하고있다 :

기타

이 목록은 내가 마지막으로 딕테이션에 넣은 위치를 보여주는 '빵 부스러기'와 같은 역할을합니다.

이렇게하려면 목록을 반복하고 dict [ "a"] [ "e"] [ "f"]와 같은 것을 생성하여 마지막 딕트를 얻는 방법이 필요합니다. 누군가가 만들었던 AutoVivification 클래스를 살펴 보았지만 매우 유용하게 보입니다.

다음 함수를 생각해 냈지만 작동하지 않습니다.

def get_nested(dict,array,i):
if i != None:
    i += 1
    if array[i] in dict:
        return get_nested(dict[array[i]],array)
    else:
        return dict
else:
    i = 0
    return get_nested(dict[array[i]],array)

도와 주셔서 감사합니다!

(나머지 매우 극히 불완전한 코드는 여기에 있습니다 :)

#Import relevant libraries
import codecs
import sys

#Functions
def stripped(str):
    if tab_spaced:
        return str.lstrip('\t').rstrip('\n\r')
    else:
        return str.lstrip().rstrip('\n\r')

def current_ws():
    if whitespacing == 0 or not tab_spaced:
        return len(line) - len(line.lstrip())
    if tab_spaced:
        return len(line) - len(line.lstrip('\t\n\r'))

def get_nested(adict,anarray,i):
    if i != None:
        i += 1
        if anarray[i] in adict:
            return get_nested(adict[anarray[i]],anarray)
        else:
            return adict
    else:
        i = 0
        return get_nested(adict[anarray[i]],anarray)

#initialise variables
jsondict = {}
unclosed_tags = []
debug = []

vividfilename = 'simple.vivid'
# vividfilename = sys.argv[1]
if len(sys.argv)>2:
    jsfilename = sys.argv[2]
else:
    jsfilename = vividfilename.split('.')[0] + '.json'

whitespacing = 0
whitespace_array = [0,0]
tab_spaced = False

#open the file
with codecs.open(vividfilename,'rU', "utf-8-sig") as vividfile:
    for line in vividfile:
        #work out how many whitespaces at start
        whitespace_array.append(current_ws())

        #For first line with whitespace, work out the whitespacing (eg tab vs 4-space)
        if whitespacing == 0 and whitespace_array[-1] > 0:
            whitespacing = whitespace_array[-1]
            if line[0] == '\t':
                tab_spaced = True

        #strip out whitespace at start and end
        stripped_line = stripped(line)

        if whitespace_array[-1] == 0:
            jsondict[stripped_line] = ""
            unclosed_tags.append(stripped_line)

        if whitespace_array[-2] < whitespace_array[-1]:
            oldnested = get_nested(jsondict,whitespace_array,None)
            print oldnested
            # jsondict.pop(unclosed_tags[-1])
            # jsondict[unclosed_tags[-1]]={stripped_line:""}
            # unclosed_tags.append(stripped_line)

        print jsondict
        print unclosed_tags

print jsondict
print unclosed_tags

해결법

  1. ==============================

    1.다음은 재귀 적 솔루션입니다. 먼저 다음과 같이 입력을 변환합니다.

    다음은 재귀 적 솔루션입니다. 먼저 다음과 같이 입력을 변환합니다.

    입력:

    person:
        address:
            street1: 123 Bar St
            street2: 
            city: Madison
            state: WI
            zip: 55555
        web:
            email: boo@baz.com
    

    첫 번째 단계 출력 :

    [{'name':'person','value':'','level':0},
     {'name':'address','value':'','level':1},
     {'name':'street1','value':'123 Bar St','level':2},
     {'name':'street2','value':'','level':2},
     {'name':'city','value':'Madison','level':2},
     {'name':'state','value':'WI','level':2},
     {'name':'zip','value':55555,'level':2},
     {'name':'web','value':'','level':1},
     {'name':'email','value':'boo@baz.com','level':2}]
    

    분할 ( ':') 및 선행 탭의 수를 계산하여 쉽게 수행 할 수 있습니다.

    def tab_level(astr):
        """Count number of leading tabs in a string
        """
        return len(astr)- len(astr.lstrip('\t'))
    

    그런 다음 첫 번째 단계 출력을 다음 함수에 제공합니다.

    def ttree_to_json(ttree,level=0):
        result = {}
        for i in range(0,len(ttree)):
            cn = ttree[i]
            try:
                nn  = ttree[i+1]
            except:
                nn = {'level':-1}
    
            # Edge cases
            if cn['level']>level:
                continue
            if cn['level']<level:
                return result
    
            # Recursion
            if nn['level']==level:
                dict_insert_or_append(result,cn['name'],cn['value'])
            elif nn['level']>level:
                rr = ttree_to_json(ttree[i+1:], level=nn['level'])
                dict_insert_or_append(result,cn['name'],rr)
            else:
                dict_insert_or_append(result,cn['name'],cn['value'])
                return result
        return result
    

    어디에:

    def dict_insert_or_append(adict,key,val):
        """Insert a value in dict at key if one does not exist
        Otherwise, convert value to list and append
        """
        if key in adict:
            if type(adict[key]) != list:
                adict[key] = [adict[key]]
            adict[key].append(val)
        else:
            adict[key] = val
    
  2. ==============================

    2.다음 코드는 블록 들여 쓰기 된 파일을 가져 와서 XML 트리로 변환합니다. 이:

    다음 코드는 블록 들여 쓰기 된 파일을 가져 와서 XML 트리로 변환합니다. 이:

    foo
    bar
    baz
      ban
      bal
    

    ...가됩니다 :

    <cmd>foo</cmd>
    <cmd>bar</cmd>
    <block>
      <name>baz</name>
      <cmd>ban</cmd>
      <cmd>bal</cmd>
    </block>
    

    기본 기술은 다음과 같습니다.

    그래서:

    from lxml import builder
    C = builder.ElementMaker()
    
    def indent(line):
        strip = line.lstrip()
        return len(line) - len(strip), strip
    
    def parse_blockcfg(data):
        top = current_block = C.config()
        stack = []
        current_indent = 0
    
        lines = data.split('\n')
        while lines:
            line = lines.pop(0)
            i, line = indent(line)
    
            if i==current_indent:
                pass
    
            elif i > current_indent:
                # we've gone down a level, convert the <cmd> to a block
                # and then save the current ident and block to the stack
                prev.tag = 'block'
                prev.append(C.name(prev.text))
                prev.text = None
                stack.insert(0, (current_indent, current_block))
                current_indent = i
                current_block = prev
    
            elif i < current_indent:
                # we've gone up one or more levels, pop the stack
                # until we find out which level and return to it
                found = False
                while stack:
                    parent_indent, parent_block = stack.pop(0)
                    if parent_indent==i:
                        found = True
                        break
                if not found:
                    raise Exception('indent not found in parent stack')
                current_indent = i
                current_block = parent_block
    
            prev = C.cmd(line)
            current_block.append(prev)
    
        return top
    
  3. ==============================

    3.우선, 배열과 dict는 파이썬에서 예약어이기 때문에 변수 이름으로 사용하지 말고 다시 사용하면 모든 종류의 혼란을 겪을 수 있습니다.

    우선, 배열과 dict는 파이썬에서 예약어이기 때문에 변수 이름으로 사용하지 말고 다시 사용하면 모든 종류의 혼란을 겪을 수 있습니다.

    좋아요. 올바르게 구하면 텍스트 파일에 트리가 있습니다. 부모가 들여 쓰기로 표시되며 실제 트리 구조를 복구하려고합니다. 권리?

    다음은 유효한 윤곽선처럼 보입니까? 현재 코드를 컨텍스트에 넣는 데 어려움이 있기 때문입니다.

    result = {}
    last_indentation = 0
    for l in f.xreadlines():
       (c, i) = parse(l) # create parse to return character and indentation
       if i==last_indentation:
       # sibling to last
       elif i>last_indentation:
       # child to last
       else:
       # end of children, back to a higher level
    

    그렇다면 귀하의 목록은 현재 부모입니다. 사실 그것은 맞습니다. 그러나 나는 문자 그대로 편지가 아닌 작성한 사전을 계속 지적 할 것입니다.

    여기서 몇 가지 일을 시작하는거야.

    result = {}
    parents = {}
    last_indentation = 1 # start with 1 so 0 is the root of tree
    parents[0] = result
    for l in f.xreadlines():
       (c, i) = parse(l) # create parse to return character and indentation
       if i==last_indentation:
           new_el = {}
           parents[i-1][c] = new_el
           parents[i] = new_el
       elif i>last_indentation:
       # child to last
       else:
       # end of children, back to a higher level
    
  4. ==============================

    4.다음은 중첩 된 Node 객체의 복합 구조에 기반한 객체 지향 접근 방식입니다.

    다음은 중첩 된 Node 객체의 복합 구조에 기반한 객체 지향 접근 방식입니다.

    입력:

    indented_text = \
    """
    apple
        colours
            red
            yellow
            green
        type
            granny smith
        price
            0.10
    """
    

    Node 클래스

    class Node:
        def __init__(self, indented_line):
            self.children = []
            self.level = len(indented_line) - len(indented_line.lstrip())
            self.text = indented_line.strip()
    
        def add_children(self, nodes):
            childlevel = nodes[0].level
            while nodes:
                node = nodes.pop(0)
                if node.level == childlevel: # add node as a child
                    self.children.append(node)
                elif node.level > childlevel: # add nodes as grandchildren of the last child
                    nodes.insert(0,node)
                    self.children[-1].add_children(nodes)
                elif node.level <= self.level: # this node is a sibling, no more children
                    nodes.insert(0,node)
                    return
    
        def as_dict(self):
            if len(self.children) > 1:
                return {self.text: [node.as_dict() for node in self.children]}
            elif len(self.children) == 1:
                return {self.text: self.children[0].as_dict()}
            else:
                return self.text
    

    텍스트를 구문 분석하려면 먼저 루트 노드를 만듭니다. 그런 다음 텍스트에서 빈 행을 제거하고 모든 행에 대해 Node 인스턴스를 만든 다음 루트 노드의 add_children 메소드에 전달합니다.

    root = Node('root')
    root.add_children([Node(line) for line in indented_text.splitlines() if line.strip()])
    d = root.as_dict()['root']
    print(d)
    

    결과:

    {'apple': [
      {'colours': ['red', 'yellow', 'green']},
      {'type': 'granny smith'},
      {'price': '0.10'}]
    }
    

    들여 쓰기 된 텍스트를 인수로 사용하여 Node의 생성자를 호출하면되는 단계에서이 작업을 수행 할 수 있어야한다고 생각합니다.

  5. from https://stackoverflow.com/questions/17858404/creating-a-tree-deeply-nested-dict-from-an-indented-text-file-in-python by cc-by-sa and MIT license