如何解析可变长度分隔文件中的数据?

时间:2010-08-12 23:43:42

标签: python file text

我有一个不符合标准的文本文件。所以我知道每个列值的(结束,开始)位置。

示例文本文件:

#     #   #   #
Techy Inn Val NJ

使用此代码找到#的位置:

  1 f = open('sample.txt', 'r')
  2 i = 0
  3 positions = []
  4 for line in f:
  5     if line.find('#') > 0:
  6         print line
  7         for each in line:
  8             i += 1
  9             if each == '#':
 10                 positions.append(i)

1 7 11 15 =>位置

到目前为止,真好!现在,如何根据我提取的位置从每一行中获取值?我正在尝试构建一个有效的循环,但任何指针都非常感谢家伙们!谢谢(:

7 个答案:

答案 0 :(得分:3)

这是使用regexp

读取固定宽度字段的方法
>>> import re
>>> s="Techy Inn Val NJ"
>>> var1,var2,var3,var4 = re.match("(.{5}) (.{3}) (.{3}) (.{2})",s).groups()
>>> var1
'Techy'
>>> var2
'Inn'
>>> var3
'Val'
>>> var4
'NJ'
>>> 

答案 1 :(得分:2)

脱离我的头顶:

f = open(.......)
header = f.next() # get first line
posns = [i for i, c in enumerate(header + "#") if c = '#']
for line in f:
    fields = [line[posns[k]:posns[k+1]] for k in xrange(len(posns) - 1)]
使用经过测试的固定代码

更新

import sys
f = open(sys.argv[1])
header = f.next() # get first line
print repr(header)
posns = [i for i, c in enumerate(header) if c == '#'] + [-1]
print posns
for line in f:
    posns[-1] = len(line)
    fields = [line[posns[k]:posns[k+1]].rstrip() for k in xrange(len(posns) - 1)]
    print fields

输入文件:

#      #  #
Foo    BarBaz
123456789abcd

调试输出:

'#      #  #\n'
[0, 7, 10, -1]
['Foo', 'Bar', 'Baz']
['1234567', '89a', 'bcd']

Robustification说明:

  1. 此解决方案适用于标题行中最后#之后的任何旧垃圾(或任何空白垃圾);它不需要用空格或其他任何东西填充标题行。
  2. 如果标题的第一个字符不是#,OP需要考虑是否是错误。
  3. 每个字段都有尾部空格剥离;这会自动从最重要的字段中删除一个尾随的换行符(如果最后一行没有被换行符终止,则不会运行。)
  4. 最终(?)更新: Leapfrooging @ gnibbler建议使用slice():在循环之前设置切片一次。

    import sys
    f = open(sys.argv[1])
    header = f.next() # get first line
    print repr(header)
    posns = [i for i, c in enumerate(header) if c == '#']
    print posns
    slices = [slice(lo, hi) for lo, hi in zip(posns, posns[1:] + [None])]
    print slices
    for line in f:
        fields = [line[sl].rstrip() for sl in slices]
        print fields
    

答案 2 :(得分:1)

改编自John Machin的回答

>>> header = "#     #   #   #"
>>> row = "Techy Inn Val NJ"
>>> posns = [i for i, c in enumerate(header) if c == '#']
>>> [row[slice(*x)] for x in zip(posns, posns[1:]+[None])]
['Techy ', 'Inn ', 'Val ', 'NJ']

您也可以像这样写下最后一行

>>> [row[i:j] for i,j in zip(posns, posns[1:]+[None])]

对于您在评论中提供的其他示例,您只需要具有正确的标题

>>> header = "#       #     #     #"
>>> row    = "Techiyi Iniin Viial NiiJ"
>>> posns = [i for i, c in enumerate(header) if c == '#']
>>> [row[slice(*x)] for x in zip(posns, posns[1:]+[None])]
['Techiyi ', 'Iniin ', 'Viial ', 'NiiJ']
>>> 

答案 3 :(得分:1)

好的,稍微不同并在评论中给出问题通用解决方案,我使用标题行而不是切片和生成器函数。另外,我通过不在第一列中输入字段名称并使用multichar字段名称而不是仅使用“#”来允许第一列进行注释。

减少的一点是,一个char字段不可能有标题名称,但在标题行中只有'#'(在以前的解决方案中总是被视为字段的开头,即使在标题中的字母后也是如此)

sample="""
            HOTEL     CAT ST DEP ##
Test line   Techy Inn Val NJ FT  FT
"""
data=sample.splitlines()[1:]

def fields(header,line):
    previndex=0
    prevchar=''
    for index,char in enumerate(header):
        if char == '#' or (prevchar != char and prevchar == ' '):
            if previndex or header[0] != ' ':
                yield line[previndex:index]
            previndex=index
        prevchar = char
    yield line[previndex:]

header,dataline = data
print list(fields(header,dataline))

输出

['Techy Inn ', 'Val ', 'NJ ', 'FT  ', 'F', 'T']

这个的一个实际用途是用于解析固定字段长度数据而不知道长度,只需将所有字段的dataline副本放在一起,不存在任何注释,并将空格替换为其他类似“_”并替换单个字符字段值通过#。

来自样本行的标题:

'            Techy_Inn Val NJ FT  ##'

答案 4 :(得分:0)

def parse(your_file):
    first_line = your_file.next().rstrip()
    slices = []
    start = None
    for e, c in enumerate(first_line):
        if c != '#':
            continue

        if start is None:
            start = e
            continue
        slices.append(slice(start, e))
        start = e
    if start is not None:
        slices.append(slice(start, None))

    for line in your_file:
        parsed = [line[s] for s in slices]
        yield parsed

答案 5 :(得分:0)

f = open('sample.txt', 'r')
pos = [m.span() for m in re.finditer('#\s*', f.next())]
pos[-1] = (pos[-1][0], None)
for line in f:
   print [line[i:j].strip() for i, j in pos]
f.close()

答案 6 :(得分:0)

这个怎么样?

with open('somefile','r') as source:
    line= source.next()
    sizes= map( len, line.split("#") )[1:]
    positions = [ (sum(sizes[:x]),sum(sizes[:x+1])) for x in xrange(len(sizes)) ] 
    for line in source:
        fields = [ line[start,end] for start,end in positions ]

这是你要找的吗?