DISPATCH
fortranfile.py
1 # Copyright 2008, 2009 Neil Martinsen-Burrell
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
9 
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
12 
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 # THE SOFTWARE.
20 
21 """Defines a file-derived class to read/write Fortran unformatted files.
22 
23 The assumption is that a Fortran unformatted file is being written by
24 the Fortran runtime as a sequence of records. Each record consists of
25 an integer (of the default size [usually 32 or 64 bits]) giving the
26 length of the following data in bytes, then the data itself, then the
27 same integer as before.
28 
29 Examples
30 --------
31 
32 To use the default endian and size settings, one can just do::
33  >>> f = FortranFile('filename')
34  >>> x = f.readReals()
35 
36 One can read arrays with varying precisions::
37  >>> f = FortranFile('filename')
38  >>> x = f.readInts('h')
39  >>> y = f.readInts('q')
40  >>> z = f.readReals('f')
41 Where the format codes are those used by Python's struct module.
42 
43 One can change the default endian-ness and header precision::
44  >>> f = FortranFile('filename', endian='>', header_prec='l')
45 for a file with little-endian data whose record headers are long
46 integers.
47 """
48 
49 __docformat__ = "restructuredtext en"
50 
51 import struct
52 import numpy
53 
54 class FortranFile(file):
55 
56  """File with methods for dealing with fortran unformatted data files"""
57 
58  def _get_header_length(self):
59  return struct.calcsize(self._header_prec)
60  _header_length = property(fget=_get_header_length)
61 
62  def _set_endian(self,c):
63  """Set endian to big (c='>') or little (c='<') or native (c='@')
64 
65  :Parameters:
66  `c` : string
67  The endian-ness to use when reading from this file.
68  """
69  if c in '<>@=':
70  self._endian = c
71  else:
72  raise ValueError('Cannot set endian-ness')
73  def _get_endian(self):
74  return self._endian
75  ENDIAN = property(fset=_set_endian,
76  fget=_get_endian,
77  doc="Possible endian values are '<', '>', '@', '='"
78  )
79 
80  def _set_header_prec(self, prec):
81  if prec in 'hilq':
82  self._header_prec = prec
83  else:
84  raise ValueError('Cannot set header precision')
85  def _get_header_prec(self):
86  return self._header_prec
87  HEADER_PREC = property(fset=_set_header_prec,
88  fget=_get_header_prec,
89  doc="Possible header precisions are 'h', 'i', 'l', 'q'"
90  )
91 
92  def __init__(self, fname, *args, **kwargs):
93  """Open a Fortran unformatted file for writing.
94 
95  Parameters
96  ----------
97  endian : character, optional
98  Specify the endian-ness of the file. Possible values are
99  '>', '<', '@' and '='. See the documentation of Python's
100  struct module for their meanings. The deafult is '>' (native
101  byte order)
102  header_prec : character, optional
103  Specify the precision used for the record headers. Possible
104  values are 'h', 'i', 'l' and 'q' with their meanings from
105  Python's struct module. The default is 'i' (the system's
106  default integer).
107 
108  """
109  if 'header_prec' in kwargs:
110  header_prec = kwargs['header_prec']
111  del(kwargs['header_prec'])
112  else:
113  header_prec='i'
114  if 'endian' in kwargs:
115  endian = kwargs['endian']
116  del(kwargs['endian'])
117  else:
118  endian='@'
119  file.__init__(self, fname, *args, **kwargs)
120  self.ENDIAN = endian
121  self.HEADER_PREC = header_prec
122 
123  def _read_exactly(self, num_bytes):
124  """Read in exactly num_bytes, raising an error if it can't be done."""
125  data = ''
126  while True:
127  l = len(data)
128  if l == num_bytes:
129  return data
130  else:
131  read_data = self.read(num_bytes - l)
132  if read_data == '':
133  raise IOError('Could not read enough data.'
134  ' Wanted %d bytes, got %d.' % (num_bytes, l))
135  data += read_data
136 
137  def _read_check(self):
138  return struct.unpack(self.ENDIAN+self.HEADER_PREC,
139  self._read_exactly(self._header_length)
140  )[0]
141 
142  def _write_check(self, number_of_bytes):
143  """Write the header for the given number of bytes"""
144  self.write(struct.pack(self.ENDIAN+self.HEADER_PREC,
145  number_of_bytes))
146 
147  def readRecord(self):
148  """Read a single fortran record"""
149  l = self._read_check()
150  data_str = self._read_exactly(l)
151  check_size = self._read_check()
152  if check_size != l:
153  raise IOError('Error reading record from data file')
154  return data_str
155 
156  def writeRecord(self,s):
157  """Write a record with the given bytes.
158 
159  Parameters
160  ----------
161  s : the string to write
162 
163  """
164  length_bytes = len(s)
165  self._write_check(length_bytes)
166  self.write(s)
167  self._write_check(length_bytes)
168 
169  def readString(self):
170  """Read a string."""
171  return self.readRecord()
172 
173  def writeString(self,s):
174  """Write a string
175 
176  Parameters
177  ----------
178  s : the string to write
179 
180  """
181  self.writeRecord(s)
182 
183  _real_precisions = 'df'
184 
185  def readReals(self, prec='f'):
186  """Read in an array of real numbers.
187 
188  Parameters
189  ----------
190  prec : character, optional
191  Specify the precision of the array using character codes from
192  Python's struct module. Possible values are 'd' and 'f'.
193 
194  """
195 
196  _numpy_precisions = {'d': numpy.float64,
197  'f': numpy.float32
198  }
199 
200  if prec not in self._real_precisions:
201  raise ValueError('Not an appropriate precision')
202 
203  data_str = self.readRecord()
204  num = len(data_str)/struct.calcsize(prec)
205  numbers =struct.unpack(self.ENDIAN+str(num)+prec,data_str)
206  return numpy.array(numbers, dtype=_numpy_precisions[prec])
207 
208  def writeReals(self, reals, prec='f'):
209  """Write an array of floats in given precision
210 
211  Parameters
212  ----------
213  reals : array
214  Data to write
215  prec` : string
216  Character code for the precision to use in writing
217  """
218  if prec not in self._real_precisions:
219  raise ValueError('Not an appropriate precision')
220 
221  # Don't use writeRecord to avoid having to form a
222  # string as large as the array of numbers
223  length_bytes = len(reals)*struct.calcsize(prec)
224  self._write_check(length_bytes)
225  _fmt = self.ENDIAN + prec
226  for r in reals:
227  self.write(struct.pack(_fmt,r))
228  self._write_check(length_bytes)
229 
230  _int_precisions = 'hilq'
231 
232  def readInts(self, prec='i'):
233  """Read an array of integers.
234 
235  Parameters
236  ----------
237  prec : character, optional
238  Specify the precision of the data to be read using
239  character codes from Python's struct module. Possible
240  values are 'h', 'i', 'l' and 'q'
241 
242  """
243  if prec not in self._int_precisions:
244  raise ValueError('Not an appropriate precision')
245 
246  data_str = self.readRecord()
247  num = len(data_str)/struct.calcsize(prec)
248  return numpy.array(struct.unpack(self.ENDIAN+str(num)+prec,data_str))
249 
250  def writeInts(self, ints, prec='i'):
251  """Write an array of integers in given precision
252 
253  Parameters
254  ----------
255  reals : array
256  Data to write
257  prec : string
258  Character code for the precision to use in writing
259  """
260  if prec not in self._int_precisions:
261  raise ValueError('Not an appropriate precision')
262 
263  # Don't use writeRecord to avoid having to form a
264  # string as large as the array of numbers
265  length_bytes = len(ints)*struct.calcsize(prec)
266  self._write_check(length_bytes)
267  _fmt = self.ENDIAN + prec
268  for item in ints:
269  self.write(struct.pack(_fmt,item))
270  self._write_check(length_bytes)
def writeReals(self, reals, prec='f')
Definition: fortranfile.py:208
def readInts(self, prec='i')
Definition: fortranfile.py:232
def readReals(self, prec='f')
Definition: fortranfile.py:185
def writeString(self, s)
Definition: fortranfile.py:173
def __init__(self, fname, args, kwargs)
Definition: fortranfile.py:92
def _write_check(self, number_of_bytes)
Definition: fortranfile.py:142
def _read_exactly(self, num_bytes)
Definition: fortranfile.py:123
def writeInts(self, ints, prec='i')
Definition: fortranfile.py:250
def writeRecord(self, s)
Definition: fortranfile.py:156