/
binaryreader.py
205 lines (157 loc) · 5.64 KB
/
binaryreader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python
"""Module for manipulating binary files, especially those with Fortran formatted
records.
"""
import struct
class _FortranRecord(object):
"""A single Fortran formatted record.
Parameters
----------
data : str
A string of binary data.
numBytes : int
Total number of bytes in record.
"""
def __init__(self, data, numBytes):
"""
Initialize instance of Record object.
"""
self.data = data
self.numBytes = numBytes
self.reset()
self.intSize = struct.calcsize('i')
self.longSize = struct.calcsize('q')
self.floatSize = struct.calcsize('f')
self.doubleSize = struct.calcsize('d')
def get_data(self, n, typeCode, itemSize):
"""
Returns one or more items of a specified type at the current
position within the data list. If more than one item is read,
the items are returned in a list.
"""
if self.pos >= self.numBytes:
raise BinaryReaderError("Already read all data from record")
values = struct.unpack('{0}{1}'.format(n,typeCode), self.data[self.pos:self.pos+itemSize*n])
self.pos += itemSize * n
return list(values)
def get_int(self, n=1):
"""
Returns one or more 4-byte integers.
"""
return self.get_data(n,'i',self.intSize)
def get_long(self, n=1):
"""
Returns one or more 8-byte integers.
"""
return self.get_data(n,'q',self.longSize)
def get_float(self, n=1):
"""
Returns one or more floats.
"""
return self.get_data(n,'f',self.floatSize)
def get_double(self, n=1):
"""
Returns one or more double
"""
return self.get_data(n,'d',self.doubleSize)
def get_string(self, length, n=1):
"""
Returns a string of a specified length starting at the current
position in the data list.
"""
if self.pos >= self.numBytes:
raise BinaryReaderError("Already read all data from record")
relevantData = self.data[self.pos:self.pos+length*n]
(s,) = struct.unpack('{0}s'.format(length*n), relevantData)
self.pos += length*n
return [s[i*length:(i+1)*length] for i in range(n)]
def put_data(self, newdata, format, itemSize):
"""
Packs a list of data objects at the current position with a
specified format and data size.
"""
try:
iter(newdata)
except TypeError:
newdata = [newdata]
for i in range(len(newdata)):
self.data += struct.pack(format,newdata[i])
self.pos += itemSize
self.numBytes += itemSize
def put_int(self, data):
"""
Pack a list of 4-byte integers.
"""
self.put_data(data,'1i',self.intSize)
def put_long(self,data):
"""
Pack a list of 8-byte integers.
"""
self.put_data(data,'1q',self.longSize)
def put_float(self,data):
"""
Pack a list of floats
"""
self.put_data(data,'1f',self.floatSize)
def put_double(self,data):
"""
Pack a list of doubles
"""
self.put_data(data,'1d',self.doubleSize)
def put_string(self, data, length, n=1):
"""
Packs a list of one or more double at the current
position within the data list.
"""
self.put_data(data,'{0}s'.format(length),length)
def reset(self):
self.pos = 0
def __repr__(self):
return "<Record: {0} bytes>".format(self.numBytes)
class _BinaryReader(object):
"""Reads/writes a binary file according to CCCC standards. This
was created following Prof. James Paul Holloway's
(hagar@umich.edu) alpha release of ccccutils written in C++ from
2001.
"""
def __init__(self,filename,mode='rb'):
self.intSize = struct.calcsize('i')
self.longSize = struct.calcsize('q')
self.f = open(filename, mode)
def close(self):
self.f.close()
def get_int(self):
(i,) = struct.unpack('i',self.f.read(self.intSize))
return i
def put_int(self,data):
self.f.write(struct.pack('i',data))
def put_fortran_record(self,record):
"""Fortran formatted records start with an integer and end with the same
integer that represents the number of bytes that the record is.
"""
self.put_int(record.numBytes)
self.f.write(record.data)
self.put_int(record.numBytes)
return 1
def get_fortran_record(self):
"""Fortran formatted records start with an integer and end with the same
integer that represents the number of bytes that the record is.
"""
numBytes = self.get_int()
# Read numBytes from the record
data = self.f.read(numBytes)
# now read end of record
numBytes2 = self.get_int()
if numBytes2 != numBytes:
raise InvalidFortranRecordError("Starting and matching integers in Fortran formatted record do not match.")
return _FortranRecord(data, numBytes)
class BinaryReaderError(Exception):
"""Case class for all binary reader errors."""
def __init__(self, msg):
self.msg = msg
def __str__(self):
return repr(self.msg)
class InvalidFortranRecordError(BinaryReaderError):
pass
class FortranRecordError(BinaryReaderError):
pass