在Python中解析大量的.dat文件并执行算术运算

在Python中解析大量的.dat文件并执行算术运算,python,parsing,Python,Parsing,我是Python新手,一般来说对编程也比较陌生。我的基本问题是解析存储在dat文件中的大型数据文件(数百万行数据) 文件中的示例数据为: 820401001 825029767710821718 8 5 510-180090000 8 9 4 820401001 8083 7970200661367 7 8 0 3-170090070 0 24 1 820401001 8082 4745200341-18 4 9 0 3 240080044 0 -20 2 820401001

我是Python新手,一般来说对编程也比较陌生。我的基本问题是解析存储在dat文件中的大型数据文件(数百万行数据)

文件中的示例数据为:

820401001 825029767710821718 8 5 510-180090000 8   9   4
820401001 8083 7970200661367 7 8 0 3-170090070 0  24   1
820401001 8082 4745200341-18 4 9 0 3 240080044 0 -20   2
820401001 8062 5805200461367 2 9 0 3 120066725 0  -7   2
820401001 8037 5292200491-17 7 7 0 3-170090070 0 -16   2
我知道以下信息:

  • 每行的长度始终为56个字符。所有字符都是数字或“-”号。本质上,数据是数字的
  • 每行有20列,列宽(即字符数)为8、1、5、5、1、2、1、2、2、3、3、1、1、2、4、4
除了解析每一行之外,我还需要对第3列和第4列执行算术运算。具体来说,我想用5个字符除以100,例如,我想要297.67而不是29767

目标是创建一个包含结果值的大规模矩阵。理想情况下,我希望将矩阵保存在一个新文件中,但我不确定如何做到这一点

所需的输出类似于:

82040100 1 82.50 297.67 71082 1 71 8 8 5 5 10 -1 800 900 0 0 8 9 4
82040100 1 80.83  79.70 20066 1 36 7 7 8 0  3 -1 700 900 7 0 0 24 1
我知道我可以使用struct库(参见下面的代码尝试),但我得到错误“unpack_from需要至少224字节的缓冲区”。我完全不知道那是什么意思

此外,我不知道如何高效地对第3列和第4列执行操作,也就是说,我可以在解析时同时执行操作,还是添加一个单独的“if-else”语句

import struct

fieldwidths = (8, 1, 5, 5, 5, 1, 2, 1, 2, 2, 2, 2, 2, 3, 3, 1, 1, 2, 4, 4) 
fmtstring = ' '.join('{}{}'.format(abs(fw), 'i') for fw in fieldwidths)
fieldstruct = struct.Struct(fmtstring)

parse = fieldstruct.unpack_from
print('fmtstring: {!r}, recsize: {} chars'.format(fmtstring, fieldstruct.size))

print("Opening the file.")
data_file = open("APR82L.dat", "r")

print("\nReading one line at a time")
#set to 10 just to test
for i in range(10):
    line = data_file.readline()
    print(line)
    fields = parse(line)
    print('fields: {}'.format(fields))

#Close the data file
print("\nClosing the data file")
data_file.close

格式字符串由56个int字段组成,每个int字段假定为4个字节长:因此,要解压缩字符串,字符串必须至少为
4*56=224
字节长。但是,传递的字符串长度为56(-ish,取决于行尾)

您可能会将数据转换为适合从传递到
struct.unpack_的格式,但真正的问题是
struct
旨在打包/解包二进制数据,而不是文本字符串。您可能会花费比实际解析输入更多的时间来准备输入。您很有可能会发现,完全避免处理
struct
,而只需自己编写一个简单的行解析器,就像这样:

col_widths = [8, 1, 5, 5, 5, 1, 2, 1, 2, 2, 2, 2, 2, 3, 3, 1, 1, 2, 4, 4]

def parse(line): # this is neither blazing fast, nor clever, but it does work.
    fields = []
    idx = 0
    for width in col_widths:
        next_idx = idx + width
        fields.append(int(line[idx:next_idx]))
        idx = next_idx
    return fields
此外,您可能希望使用一个简单的检查来确保每一行都值得解析:

with open('APR82L.dat') as data_file:
    for line in data_file: # This is the normal way to read a file line by line
        if line.strip(): # if the line isn't empty:
            fields = parse(line)

至于做算术,只要有意义就做。如果这是一个相对简单的操作,我建议编写一个函数来执行任何需要执行的操作,并在读取数据时调用该函数

def calculate(fields):
    x = fields[2] # third field
    y = fields[3] # fourth field
    return x + y # or whatever

with open('APR82L.dat') as data_file:
    for line in data_file:
        # parse line into fields as above, then:

        result = calculate(fields)
        # then write the result someplace or whatever's appropriate
如果您不介意大多数面向科学计算的人已经安装了相对轻量级的依赖项,那么设计的就是为了实现这一点

步骤1:

将固定宽度格式解析为逗号或用cut、awk和/或sed分隔的空格

步骤2:

import numpy as np
data = np.loadtxt('parsed.txt')
添加第2列和第3列将非常简单

output = data[:,2] + data[:,3]  

或者,您可以使用函数一步完成解析和numpy排列。

您可以执行简单的字符串处理。下面是一个示例解析器,它应该做您想要做的事情:

class Parser(object):
    def __init__(self, fd):
        self.widths = (8, 1, 3, 2, 3, 2, 5, 1, 2, 1, 2, 2, 2, 2, 2,
                       3, 3, 1, 1, 2, 4, 4)
        self.seps = '  . .                \n'
        self.fd = fd
    def parse(self, filename):
        with open(filename) as file:
            for line in file:
                l = []
                ix = 0
                out = ''
                rank=0
                for j in self.widths:
                    l.append(line[ix:ix+j])
                    out = out + str(int(line[ix:ix+j])) + self.seps[rank]
                    rank += 1
                    ix += j
                self.fd.write(out)
您可以使用文件对象(例如sys.stdout)作为参数来创建解析器。此文件对象将接收输出,您使用输入文件名作为参数调用parse:

import sys
parser = Parser(sys.stdout)
parser.parse('input.dat')
如果要在另一个文件上写入:

with open('outfile.dat', 'w') ad fd:
    parser = Parser(fd)
    parser.parse('input.dat')

请注意:如果您有GNU awk(如果您使用的是GNU/linux发行版,您将有GNU awk),那么您可以从bash提示符轻松地完成此转换

awk -vFIELDWIDTHS="8 1 5 5 5 1 2 1 2 2 2 2 2 3 3 1 1 2 4 4" \
    '{$3/=100;$4/=100;print}' \
    < input.dat > newfile.dat
awk-vFIELDWIDTHS=“8 1 5 5 1 2 2 2 2 3 1 2 4”\
“{$3/=100;$4/=100;打印}”\
newfile.dat

如果您想在输出中用制表符分隔字段,请在
-vFIELDWIDTHS=…
赋值之前添加
-vOFS='\t'

首先,您有一个ascii数据表,为此,一个很好的工具是Python内置的字符串操作(更多信息,请参见底部)。此外,您的数据并没有那么“海量”,因此您应该从编写代码开始,这样做很容易,然后在需要时进行优化

这里有一个小Python程序,可以进行解析,希望是自解释的。我从一个字符串开始定义结构,这是我刚刚编出来的一个简单的方法来定义它

structure = "8i 1i 5f 5f 5i 1i 2i 1i 2i 2i 2i 2i 2i 3i 3i 1i 1i 2i 4i 4i"
structure = structure.split()

result = []
with open("data.txt") as df:
    for line in df.readlines():
        n, vals = 0, []
        for s in structure:
            width = int(s[0])
            val = int(line[n:n+width])
            if s[1]=='f':
                val = val/100.
            vals.append(val)
            n += width
        result.append(vals)
这使得:

result = [
[82040100, 1, 82.5, 297.67, 71082, 1, 71, 8, 8, 5, 5, 10, -1, 800, 900, 0, 0, 8, 9, 4]
[82040100, 1, 80.83, 79.7, 20066, 1, 36, 7, 7, 8, 0, 3, -1, 700, 900, 7, 0, 0, 24, 1]
[82040100, 1, 80.82, 47.45, 20034, 1, -1, 8, 4, 9, 0, 3, 2, 400, 800, 4, 4, 0, -20, 2]
[82040100, 1, 80.62, 58.05, 20046, 1, 36, 7, 2, 9, 0, 3, 1, 200, 667, 2, 5, 0, -7, 2]
[82040100, 1, 80.37, 52.92, 20049, 1, -1, 7, 7, 7, 0, 3, -1, 700, 900, 7, 0, 0, -16, 2]]

struct
主要用于解析二进制数据,因此尽管您可以使用它,但它不是最佳选择。另外,numpy的loadtxt需要一些类型定界器,您没有这样的定界器,因此也无法工作(除非您预先解析数据,这似乎不符合要点)。

您的数据是以二进制格式还是ascii格式存储的?例如,您是如何看待所显示的示例数据的?(因为你显示的是奇怪的间距,我假设是ascii码,但我想在写答案之前验证一下。)关键问题:你需要它快吗?换句话说,脚本是否会运行:只运行一次?每个月?每隔10秒?
struct.unpack
用于解码二进制数据,而不是用于拆分字符串。在Python中,需要添加括号才能调用方法:
data\u file.close()
我显示的数据与我在Sublime中看到的数据完全相同。解决方案确实需要快速-这将每天运行,一天几次。