【问题标题】:Python convert table to dictionaryPython将表转换为字典
【发布时间】:2015-09-10 20:01:53
【问题描述】:

问题:命令生成的表格很难编写脚本。

解决方案:将表格转换为 Python 字典以提高使用效率。这些表可以有 1 - 20 个不同的虚拟驱动器,并且可能无法设置“名称”等属性。

示例表:

Virtual Drives = 4

VD LIST :
=======

----------------------------------------------------------
DG/VD TYPE   State Access Consist Cache sCC     Size Name
----------------------------------------------------------
0/0   RAID1  Optl  RW     No      RWTD  -   1.818 TB one
1/1   RAID1  Optl  RW     No      RWTD  -   1.818 TB two
2/2   RAID1  Optl  RW     No      RWTD  -   1.818 TB three
3/3   RAID1  Optl  RW     No      RWTD  -   1.818 TB four
4/4   RAID10 Reblg RW     No      RWTD  -   4.681 TB 
----------------------------------------------------------

词典示例:

{"DG/VD":"0/0", "TYPE":"RAID1", "State":"Optl", "Access":"RW", "Consist":"No", "Cache":"RWTD", "sCC":"-", "Size":"1.818 TB", "Name":"one"}
{"DG/VD":"4/4", "TYPE":"RAID10", "State":"Reblg", "Access":"RW", "Consist":"No", "Cache":"RWTD", "sCC":"-", "Size":"4.681 TB", "Name":""}

总共有四个字典,每个虚拟驱动器一个。如何最好地解决这个问题?

我有一些想法。首先,搜索表头并拆分空格以定义列表。其次,使用“编号/编号”搜索虚拟驱动器并按空格分割以定义第二个列表。但是,“Size”需要特殊,因为它需要忽略数字和“TB”之间的空格。

接下来,将两个列表压缩在一起以生成字典。有没有人有更好的想法来处理这个文本?

# Create a list of all the headers in the virtual disk table
get_table_header = " /c0/vall show | awk '/^DG\/VD/'"
table_header_values = console_info(utility + get_table_header).split()

['DG/VD', 'TYPE', 'State', 'Access', 'Consist', 'Cache', 'sCC', 'Size', 'Name']

# Create a list of all virtual drives
get_virtual_drives = " /c0/vall show | awk '/^[0-9]\/[0-9]/'"
virtual_drive_values = console_info(utility + get_virtual_drives).split()

["0/0", "RAID1", "Optl", "RW", "No", "RWTD", "-", "1.818", "TB", "0"]

【问题讨论】:

  • 列标题没有一致的对齐方式太糟糕了,否则很容易将它们用作分割位置的指导。如果只是“大小”由于某种原因未与其列的右侧对齐...
  • 如果您希望每个 HD 的这些属性中的每一个都与 HD 相关联,那么您需要一个 HD 列表,并将字典作为每个 HD 的元素。
  • 先做点工作,然后再改进。
  • Size 总是在那里吗?
  • 好的,编辑后的代码应该可以处理所有情况

标签: python list dictionary


【解决方案1】:
from itertools import dropwhile, takewhile
with open("test.txt") as f:
    dp = dropwhile(lambda x: not x.startswith("-"), f)
    next(dp)  # skip ----
    names = next(dp).split()  # get headers names
    next(f)  # skip -----
    out = []
    for line in takewhile(lambda x: not x.startswith("-"), f):
        a, b = line.rsplit(None, 1)
        out.append(dict(zip(names, a.split(None, 7) + [b])))]

输出:

from pprint import  pprint as pp

pp(out)
[{'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '0/0',
  'Name': 'one',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '1/1',
  'Name': 'two',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '2/2',
  'Name': 'three',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '3/3',
  'Name': 'four',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'}]

如果您想保持秩序,请使用 OrderedDict

out = [OrderedDict(zip(names, line.split()))
           for line in takewhile(lambda x: not x.startswith("-"), f)]

根据您的编辑缺少名称值:

from itertools import dropwhile, takewhile

with open("test.txt") as f:
    dp = dropwhile(lambda x: not x.startswith("-"), f)
    next(dp)  # skip ----
    names = next(dp).split()  # get headers names
    next(f)  # skip -----
    out = []
    for line in takewhile(lambda x: not x.startswith("-"), f):
        a, b = line.rsplit(" ", 1)
        out.append(dict(zip(names,  a.rstrip().split(None, 7) + [b.rstrip()])))

输出:

[{'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '0/0',
  'Name': 'one',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '1/1',
  'Name': 'two',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '2/2',
  'Name': 'three',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '3/3',
  'Name': 'four',
  'Size': '1.818 TB',
  'State': 'Optl',
  'TYPE': 'RAID1',
  'sCC': '-'},
 {'Access': 'RW',
  'Cache': 'RWTD',
  'Consist': 'No',
  'DG/VD': '4/4',
  'Name': '',
  'Size': '4.681 TB',
  'State': 'Reblg',
  'TYPE': 'RAID10',
  'sCC': '-'}]

这也将处理在 TB 和 Name 列值 1.818 TB one 之间有多个空格的行

【讨论】:

  • 它应该是'Size': '1.818 TB', 'Name': 'one',而不是'Name': 'TB'
  • @BrentWashburne,现在可以了
【解决方案2】:

您可以使用struct 模块来解析表格行中的数据,如下所示,该模块将数据存储在OrderedDict 中以保留生成的字典中的字段顺序,但这样做是可选的。 “名称”字段不必存在。

from __future__ import print_function
from collections import OrderedDict
import json  # for pretty-printing results
import struct
from textwrap import dedent

table = dedent("""
    Virtual Drives = 4

    VD LIST :
    =======

    ----------------------------------------------------------
    DG/VD TYPE  State Access Consist Cache sCC     Size Name
    ----------------------------------------------------------
    0/0   RAID1 Optl  RW     No      RWTD  -   1.818 TB one
    1/1   RAID1 Optl  RW     No      RWTD  -   1.818 TB two
    2/2   RAID1 Optl  RW     No      RWTD  -   1.818 TB three
    3/3   RAID1 Optl  RW     No      RWTD  -   1.818 TB four
    ----------------------------------------------------------
""")

# negative widths represent ignored padding fields
fieldwidths = 3, -3, 5, -1, 4, -2, 2, -5, 3, -5, 4, -2, 3, -1, 8, -1, 5
fmtstring = ' '.join('{}{}'.format(abs(fw), 'x' if fw < 0 else 's')
                        for fw in fieldwidths)
fieldstruct = struct.Struct(fmtstring)
parse = fieldstruct.unpack_from

itable = iter(table.splitlines())
for line in itable:
    if line.startswith('-----'):
        break

fieldnames = next(itable).split()

for line in itable:
    if line.startswith('-----'):
        break

for line in itable:
    if line.startswith('-----'):
        break
    if len(line) < fieldstruct.size:
        line += ' ' * (fieldstruct.size - len(line))
    fields = tuple(field.strip() for field in parse(line))
    rec = OrderedDict(zip(fieldnames, fields))
    print(json.dumps(rec))

输出:

{"DG/VD": "0/0", "TYPE": "RAID1", "State": "Optl", "Access": "RW", 
 "Consist": "No", "Cache": "RWTD", "sCC": "-", "Size": "1.818 TB", 
 "Name": "one"}
{"DG/VD": "1/1", "TYPE": "RAID1", "State": "Optl", "Access": "RW", 
 "Consist": "No", "Cache": "RWTD", "sCC": "-", "Size": "1.818 TB", 
 "Name": "two"}
{"DG/VD": "2/2", "TYPE": "RAID1", "State": "Optl", "Access": "RW", 
 "Consist": "No", "Cache": "RWTD", "sCC": "-", "Size": "1.818 TB", 
 "Name": "three"}
{"DG/VD": "3/3", "TYPE": "RAID1", "State": "Optl", "Access": "RW", 
 "Consist": "No", "Cache": "RWTD", "sCC": "-", "Size": "1.818 TB", 
 "Name": "four"}

【讨论】:

  • 结构体的有趣使用
  • @Padraic:谢谢。这只是my answer 对问题Efficient way of parsing fixed width files in Python 的简单改编,以使其处理可变长度的问题。
  • 我认为应该是..., "sCC": "-", "Size": "1.818 TB", "Name": "one"。此输出显示 sCCSize 的不同值,并且没有 Name
  • @Brent:糟糕——我错误地将“sCC”和“Size”字段合并在一起。感谢您指出有问题。
  • 虽然这确实可行,但我编辑了建议的解决方案以读取表格值是可变的并且没有设置宽度。
【解决方案3】:

一种方法是将表格读入 pandas DataFrame 并使用其 to_dict 方法将其转换为字典。

这需要将原始表复制到文件中,删除虚线,然后在标题之后添加一个额外的列大小可能称为单位以容纳“TB”数据,或者不添加额外的列并删除或替换每个 Size datum 和 'TB' 之间的空格(可能用破折号)。

然后可以使用 df.read_csv 方法将文件作为 '\s+' 分隔的 csv 文件加载到 pandas DataFrame (df) 中,并使用 df.T 将其转置转换为 dict(与 df.transpose 相同) 和 df.to_dict 方法。二维表的转置只是将其列和行互换,即列变为行,行变为列。

以 table.txt 开头,包含:

DG/VD TYPE  State Access Consist Cache sCC     Size Units Name
0/0   RAID1 Optl  RW     No      RWTD  -   1.818 TB one
1/1   RAID1 Optl  RW     No      RWTD  -   1.818 TB two
2/2   RAID1 Optl  RW     No      RWTD  -   1.818 TB three
3/3   RAID1 Optl  RW     No      RWTD  -   1.818 TB four 

以下代码将其转换为名为 table_dict 的字典:

import pandas as pd
table_dict = pd.read_csv('table.txt',sep='\s+').to_dict(orient= 'index')

table_dict 由 4 个字典组成,键在 range(4) 中,如下所示:

import pprint
pprint.pprint(table_dict)

{0: {'Access': 'RW',
     'Cache': 'RWTD',
     'Consist': 'No',
     'DG/VD': '0/0',
     'Name': 'one',
     'Size': 1.818,
     'State': 'Optl',
     'TYPE': 'RAID1',
     'Units': 'TB',
     'sCC': '-'},
 1: {'Access': 'RW',
     'Cache': 'RWTD',
     'Consist': 'No',
     'DG/VD': '1/1',
     'Name': 'two',
     'Size': 1.818,
     'State': 'Optl',
     'TYPE': 'RAID1',
     'Units': 'TB',
     'sCC': '-'},
 2: {'Access': 'RW',
     'Cache': 'RWTD',
     'Consist': 'No',
     'DG/VD': '2/2',
     'Name': 'three',
     'Size': 1.818,
     'State': 'Optl',
     'TYPE': 'RAID1',
     'Units': 'TB',
     'sCC': '-'},
 3: {'Access': 'RW',
     'Cache': 'RWTD',
     'Consist': 'No',
     'DG/VD': '3/3',
     'Name': 'four',
     'Size': 1.818,
     'State': 'Optl',
     'TYPE': 'RAID1',
     'Units': 'TB',
     'sCC': '-'}}

pandas DataFrame 有其他方法可以转换为其他格式,包括 JSON、HTML、SQL 等。

【讨论】:

    【解决方案4】:

    你可以试试这样的:

    import re
    
    lines = re.split("\n", data)
    
    for line in lines[8:-1]:
        fields = re.split("  +",line)
        print(fields)
    

    data 包含您的表格。 要遵循的登录是将表格拆分为单行,然后使用两个或多个空格作为分隔符将每一行拆分为字段(注意re.split(" +", line))。诀窍是从第 8 行开始,到最后 1 行结束。

    将单行拆分为包含字段的列表后,构建字典很简单

    【讨论】:

      【解决方案5】:
      import re
      
      table = '''Virtual Drives = 4
      
      VD LIST :
      =======
      
      ----------------------------------------------------------
      DG/VD TYPE  State Access Consist Cache sCC     Size Name
      ----------------------------------------------------------
      0/0   RAID1 Optl  RW     No      RWTD  -   1.818 TB one
      1/1   RAID1 Optl  RW     No      RWTD  -   1.818 TB two
      2/2   RAID1 Optl  RW     No      RWTD  -   1.818 TB three
      3/3   RAID1 Optl  RW     No      RWTD  -   1.818 TB four
      ----------------------------------------------------------'''
      table = table.split('\n')
      
      result = []
      header = None
      divider_pattern = re.compile('^[-]{20,}$')
      for i, row in enumerate(table):
          row = row.strip()
          if divider_pattern.match(row) and not header:
              header = table[i + 1].split()
              continue
          if header and not divider_pattern.match(row):
              row = row.split()
              if row != header:
                  result.append(dict(zip(header, row)))
      
      print result
      

      【讨论】:

        【解决方案6】:

        为此有一个 python 库:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_fwf.html

         import pandas as pd
        
         data = pd.read_fwf('filename.txt')
        

        但是您必须将表格预处理为这种格式:

        DG/VD TYPE  State Access Consist Cache sCC     Size Units Name
        0/0   RAID1 Optl  RW     No      RWTD  -   1.818 TB one
        1/1   RAID1 Optl  RW     No      RWTD  -   1.818 TB two
        2/2   RAID1 Optl  RW     No      RWTD  -   1.818 TB three
        3/3   RAID1 Optl  RW     No      RWTD  -   1.818 TB four 
        

        【讨论】:

          猜你喜欢
          • 2015-07-23
          • 1970-01-01
          • 2015-11-14
          • 2016-10-25
          • 1970-01-01
          • 2022-12-04
          • 1970-01-01
          • 2011-12-08
          • 1970-01-01
          相关资源
          最近更新 更多