如何在括号和缺失值之间用逗号分析CSV

时间:2017-05-31 16:04:59

标签: python csv pyparsing

我尝试使用pyparsing解析CSV:

  • 括号(或括号等)之间的逗号:" a(1,2),b"应该返回清单[" a(1,2)"," b"]
  • 缺少值:" a,b ,, c,"应该返回列表[' a',''''' c','']

我做了一个解决方案,但似乎"脏"。主要是Optional里面只有一个可能的原子。我认为可选项应该独立于原子。也就是说,我觉得它应该放在其他地方,例如在delimitedList可选参数中,但在我的试验和错误中,这是唯一有效且有意义的地方。它可能在任何可能的原子中,所以我选择了第一个。

另外,我并不完全理解originalTextFor正在做什么,但如果我删除它就会停止工作。

工作示例:

import pyparsing as pp

# Function that parses a line of columns separated by commas and returns a list of the columns
def fromLineToRow(line):
    sqbrackets_col = pp.Word(pp.printables, excludeChars="[],") | pp.nestedExpr(opener="[",closer="]")  # matches "a[1,2]"
    parens_col = pp.Word(pp.printables, excludeChars="(),") | pp.nestedExpr(opener="(",closer=")")      # matches "a(1,2)"
    # In the following line:
    # * The "^" means "choose the longest option"
    # * The "pp.Optional" can be in any of the expressions separated by "^". I put it only on the first. It's used for when there are missing values
    atomic = pp.originalTextFor(pp.Optional(pp.OneOrMore(parens_col))) ^ pp.originalTextFor(pp.OneOrMore(sqbrackets_col))

    grammar = pp.delimitedList(atomic)

    row = grammar.parseString(line).asList()
    return row

file_str = \
"""YEAR,a(2,3),b[3,4]
1960,2.8,3
1961,4,
1962,,1
1963,1.27,3"""

for line in file_str.splitlines():
    row = fromLineToRow(line)
    print(row)

打印:

['YEAR', 'a(2,3)', 'b[3,4]']
['1960', '2.8', '3']
['1961', '4', '']
['1962', '', '1']
['1963', '1.27', '3']

这是正确的方法吗?有没有"清洁"在第一个原子中使用Optional的方法吗?

3 个答案:

答案 0 :(得分:1)

从内到外工作,我明白了:

# chars not in ()'s or []'s - also disallow ','
non_grouped = pp.Word(pp.printables, excludeChars="[](),")

# grouped expressions in ()'s or []'s
grouped = pp.nestedExpr(opener="[",closer="]") | pp.nestedExpr(opener="(",closer=")")

# use OneOrMore to allow non_grouped and grouped together
atomic = pp.originalTextFor(pp.OneOrMore(non_grouped | grouped))
# or based on your examples, you *could* tighten this up to:
# atomic = pp.originalTextFor(non_grouped + pp.Optional(grouped))

originalTextFor重新组合匹配表达式的前导和尾随边界内的原始输入文本,并返回单个字符串。如果将其保留,则将在嵌套的字符串列表中获取所有子表达式,例如['a',['2,3']]。你可以重新加入他们,重复调用''.join,但这会崩溃空白(或使用' '.join,但这有可能引入空格的相反问题。)

要自选列表的元素,只需在分隔列表的定义中这样说:

grammar = pp.delimitedList(pp.Optional(atomic, default=''))

请务必添加默认值,否则空插槽将被删除。

有了这些改变,我得到了:

['YEAR', 'a(2,3)', 'b[3,4]']
['1960', '2.8', '3']
['1961', '4', '']
['1962', '', '1']
['1963', '1.27', '3']

答案 1 :(得分:0)

您可以使用正则表达式re,例如:

>>> import re
>>> re.split(r',\s*(?![^()]*\))', line1)
['a(1,2)', 'b']
>>> re.split(r',\s*(?![^()]*\))', line2)
['a', 'b', '', 'c', '']

答案 2 :(得分:0)

import re

with open('44289614.csv') as f:
    for line in map(str.strip, f):
        l = re.split(',\s*(?![^()[]]*[\)\]])', line)
        print(len(l), l)

输出:

3 ['YEAR', 'a(2,3)', 'b[3,4]']
3 ['1960', '2.8', '3']
3 ['1961', '4', '']
3 ['1962', '', '1']
3 ['1963', '1.27', '3']

this answer修改。

我也喜欢this answer,建议稍微修改输入并使用csv模块的quotechar