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

Python 如何解析可变长度分隔文件中的数据?,python,file,text,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

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

示例文本文件:

#     #   #   #
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=>位置


到目前为止,一切都很好!现在,如何根据我获取的位置从每一行获取值?我正在尝试构建一个有效的循环,但任何指针都是非常受欢迎的!谢谢(:

这里有一种使用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'
>>> 

下面是一种使用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'
>>> 

不经意间:

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']
坚固性说明:

  • 此解决方案适用于标题行中最后一个
    #
    之后的任何旧垃圾(或什么都没有);它不需要在标题行中填充空格或其他任何内容
  • 如果头的第一个字符不是< > > < < /C> >,OP需要考虑它是否是一个错误。
  • 每个字段都去除了尾随空格;这会自动从rihtmost字段中删除尾随换行符(如果最后一行未以换行符终止,则不会运行amok)
  • 最终(?)更新:Leapfrooging@gnibler建议使用
    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
    

    不经意间:

    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']
    
    坚固性说明:

  • 此解决方案适用于标题行中最后一个
    #
    之后的任何旧垃圾(或什么都没有);它不需要在标题行中填充空格或其他任何内容
  • 如果头的第一个字符不是< > > < < /C> >,OP需要考虑它是否是一个错误。
  • 每个字段都去除了尾随空格;这会自动从rihtmost字段中删除尾随换行符(如果最后一行未以换行符终止,则不会运行amok)
  • 最终(?)更新:Leapfrooging@gnibler建议使用
    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
    

    改编自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']
    >>> 
    

    改编自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']
    >>> 
    
    这个怎么样

    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 ]
    
    这就是你要找的吗?

    这个怎么样

    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 ]
    

    这就是你要找的吗?

    好的,为了有点不同并给出一般化的注释解决方案,我使用了标题行而不是slice和generator函数。此外,我还允许通过不在第一列中放置字段名以及使用多字符字段名而不是仅使用“#”来对第一列进行注释

    缺点是一个字符字段不可能有标题名,而只能在标题行中有“#”(这在以前的解决方案中被视为字段的开头,甚至在标题中的字母之后)

    输出

    ['Techy Inn ', 'Val ', 'NJ ', 'FT  ', 'F', 'T']
    
    这种方法的一个实际用途是在不知道长度的情况下解析固定字段长度的数据,只需将数据行的副本放在所有字段中,不存在任何注释,并将空格替换为“_”,将单字符字段值替换为#

    来自采样行的标题:

    '            Techy_Inn Val NJ FT  ##'
    

    好的,为了有一点不同,并给出一般化的注释解决方案,我使用了标题行而不是slice和generator函数。此外,我允许第一列作为注释,不将字段名放在第一列中,并使用multichar字段名而不是仅使用“#”

    缺点是一个字符字段不可能有标题名,而只能在标题行中有“#”(这在以前的解决方案中被视为字段的开头,甚至在标题中的字母之后)

    输出

    ['Techy Inn ', 'Val ', 'NJ ', 'FT  ', 'F', 'T']
    
    这种方法的一个实际用途是在不知道长度的情况下解析固定字段长度的数据,只需将数据行的副本放在所有字段中,不存在任何注释,并将空格替换为“_”,将单字符字段值替换为#

    来自采样行的标题:

    '            Techy_Inn Val NJ FT  ##'
    

    zip中x的
    fields=[line[slice(*x)](posns,posns[1:])如何
    这个答案非常准确,先生!效果很好。另一个挑战是,如果分隔符是3个字母,但并非所有字母都相同,例如:我知道它要求的太多了!智能地解析分隔符将非常棒。再次感谢……哇!我稍微调整了我的程序,使用我的代码查找分隔符d将这些位置传递给你的代码和boom,这很有效!非常感谢你!@gnibbler:感谢你建议使用切片。@匿名DriveByDownvoter:请给出原因,以便我/我们可以从你的智慧中获益?对于zip中的x(posns,posns[1:]),如何
    fields=[line[slice(*x)]
    这个答案非常准确,先生!效果很好。另一个挑战是,如果分隔符是3个字母,但并非所有字母都相同,例如:我知道它要求的太多了!智能地解析分隔符将非常棒。再次感谢……哇!我稍微调整了我的程序,使用我的代码查找分隔符d将位置传递给您的代码,boom,它起作用了!非常感谢!@gnibbler:感谢您提出使用切片的建议。@AnonymousDriveByDownVoter:请给出原因,以便我/我们可以从您的智慧中获益?非常优雅!您能解决以下问题吗:var1,var2=re.match((.{%d})(.d}),第2行)。groups()%(位置[1],位置(2)属性错误:“NoType”对象没有属性“组”,ReGEXP不匹配行。而且,我很好奇为什么您认为ReExxPs更优雅。