将带有类JSON对象的文本文件解析为CSV

问题描述:

我有一个包含键值对的文本文件,最后两个键值对包含类似JSON的对象,我想将其拆分为列并使用其他值,使用键作为列标题。前三行中的数据文件input.txt的是这样的:将带有类JSON对象的文本文件解析为CSV

InnerDiameterOrWidth::0.1,InnerHeight::0.1,Length2dCenterToCenter::44.6743867864386,Length3dCenterToCenter::44.6768028159989,Tag::<NULL>,{StartPoint::7858.35924983374[%2C]1703.69341358077[%2C]-3.075},{EndPoint::7822.85045874375[%2C]1730.80294308742[%2C]-3.53962362760298} 
InnerDiameterOrWidth::0.1,InnerHeight::0.1,Length2dCenterToCenter::57.8689351603823,Length3dCenterToCenter::57.8700464193429,Tag::<NULL>,{StartPoint::7793.52927597915[%2C]1680.91224357457[%2C]-3.075},{EndPoint::7822.85045874375[%2C]1730.80294308742[%2C]-3.43363070193163} 
InnerDiameterOrWidth::0.1,InnerHeight::0.1,Length2dCenterToCenter::68.7161350545728,Length3dCenterToCenter::68.7172034962765,Tag::<NULL>,{StartPoint::7858.35924983374[%2C]1703.69341358077[%2C]-3.075},{EndPoint::7793.52927597915[%2C]1680.91224357457[%2C]-3.45819643838485} 

,我们终于想出了一些工作,但必须有一个更好的方法:

import csv 
with open('input.txt', 'rb') as fin, open('output.csv', 'wb') as fout: 
    reader = csv.reader(fin) 
    writer = csv.writer(fout) 
    for i, line in enumerate(reader): 
     mysplit = [item.split('::') for item in line if item.strip()] 
     if not mysplit: # blank line 
      continue 
     keys, vals = zip(*mysplit) 
     start_vals = [item.split('[%2C]') for item in mysplit[-2]] 
     end_vals = [item.split('[%2C]') for item in mysplit[-1]] 
     a=list(keys[0:-2]) 
     a.extend(['start1','start2','start3','end1','end2','end3']) 
     b=list(vals[0:-2]) 
     b.append(start_vals[1][0]) 
     b.append(start_vals[1][1]) 
     b.append(start_vals[1][2][:-1]) 
     b.append(end_vals[1][0]) 
     b.append(end_vals[1][1]) 
     b.append(end_vals[1][2][:-1]) 
     if i == 0: 
      # if first line: write header 
      writer.writerow(a) 
     writer.writerow(b) 

产生输出文件output.csv,看起来像这样

InnerDiameterOrWidth,InnerHeight,Length2dCenterToCenter,Length3dCenterToCenter,Tag,start1,start2,start3,end1,end2,end3 
0.1,0.1,44.6743867864386,44.6768028159989,<NULL>,7858.35924983374,1703.69341358077,-3.075,7822.85045874375,1730.80294308742,-3.53962362760298 
0.1,0.1,57.8689351603823,57.8700464193429,<NULL>,7793.52927597915,1680.91224357457,-3.075,7822.85045874375,1730.80294308742,-3.43363070193163 
0.1,0.1,68.7161350545728,68.7172034962765,<NULL>,7858.35924983374,1703.69341358077,-3.075,7793.52927597915,1680.91224357457,-3.45819643838485 

我们不想写这样的代码在未来。

什么是阅读这样的数据的最佳方式是什么?

+1

没有什么类似JSON的输入格式。唯一与远程相关的思想是花括号和逗号,但比较结束。 – 2013-03-04 21:10:33

+1

我想几天前有人问过这样的问题,我会尽力找到它。或者,也许这是这个问题的延续?编辑:(http://*.com/questions/15190260/python-csv-read-write-remove-and-replace-plus-end-of-line-is-json-format/15190741) – daveydave400 2013-03-04 21:25:29

我会使用:

from itertools import chain 
import csv 

_header_translate = { 
    'StartPoint': ('start1', 'start2', 'start3'), 
    'EndPoint': ('end1', 'end2', 'end3') 
} 

def header(col): 
    header = col.strip('{}').split('::', 1)[0] 
    return _header_translate.get(header, (header,)) 

def cleancolumn(col): 
    col = col.strip('{}').split('::', 1)[1] 
    return col.split('[%2C]') 

def chainedmap(func, row): 
    return list(chain.from_iterable(map(func, row))) 

with open('input.txt', 'rb') as fin, open('output.csv', 'wb') as fout: 
    reader = csv.reader(fin) 
    writer = csv.writer(fout) 
    for i, row in enumerate(reader): 
     if not i: # first row, write header first 
      writer.writerow(chainedmap(header, row)) 
     writer.writerow(chainedmap(cleancolumn, row)) 

cleancolumn方法需要你的任何列,并且去掉括号,第一::和分裂的前去除一切后,返回一个元组(可能只有一个值)嵌入'逗号'。通过使用itertools.chain.from_iterable(),我们再次将csv编写器中从列中生成的一系列元组转换为一个列表。

当处理第一行时,我们从相同的列中生成一个标题行,将StartPointEndPoint标题替换为6个扩展标题。

+0

好的答案。任何你喜欢的原因'如果不是我'结束'如果我=='?对我来说,你并不是问“它是否存在?”,你正在检查它是否为值0. – daveydave400 2013-03-04 21:34:48

+0

@ daveydave400:Python中的numeric 0总是为false。所以是空序列和集合(字典,集合,列表,元组,字符串等)。我的眼睛更轻松,更清洁。 – 2013-03-04 21:35:12

+0

我知道这是准确的,并且空序列评估为false,当我真正检查一个值时,我个人更喜欢“== X”,并且想知道除了偏好之外是否还有其他原因。当我检查空序列时,如果检查无是“无”或“不是无”,则使用“不是X”。 – daveydave400 2013-03-04 21:39:43