chunkfile.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Created on 31.07.2012
  4. @author: frank
  5. '''
  6. import struct
  7. from array import array
  8. import numpy
  9. import logging
  10. def fwrite(filename, formatstring, ndarray):
  11. """
  12. from http://stackoverflow.com/questions/10637376/import-error-no-module-named-numpyio
  13. because numpyio was removed in SciPy 0.9
  14. """
  15. arr = array.array(formatstring, ndarray.flatten())
  16. f = open(filename, 'w')
  17. arr.tofile(f)
  18. f.close()
  19. class NoChunkNameFound(Exception):
  20. pass
  21. class NoMoreChunksFound(Exception):
  22. pass
  23. class UnexpectedEndOfChunkFile(Exception):
  24. pass
  25. class ChunkNotFound(Exception):
  26. pass
  27. class WrongElementSize(Exception):
  28. pass
  29. def write_name_string(file_obj, s):
  30. file_obj.write(struct.pack('B', len(s)))
  31. file_obj.write(s.encode('ascii'))
  32. class FileFormat(object):
  33. def __init__(self, name, major, minor):
  34. self.name = name
  35. self.major = major
  36. self.minor = minor
  37. def write(self, file_obj):
  38. write_name_string(file_obj, self.name)
  39. file_obj.write(struct.pack('ii', self.major, self.minor))
  40. class ChunkHeader(object):
  41. def __init__(self, name, chunk_size, element_size, n_elements):
  42. self.name = name
  43. self.chunk_size = chunk_size
  44. self.element_size = element_size
  45. self.n_elements = n_elements
  46. def get_data_size(self):
  47. return self.element_size * self.n_elements
  48. def get_header_size(self):
  49. return 1 + len(self.name) + 3*8
  50. def get_total_chunksize(self):
  51. return self.get_header_size() + self.get_data_size()
  52. def get_data_pos(self, chunk_pos):
  53. return chunk_pos + self.get_header_size()
  54. def write(self, file_obj):
  55. write_name_string(file_obj, self.name)
  56. file_obj.write(struct.pack('QQQ', self.chunk_size, self.element_size, self.n_elements))
  57. datatype_size_map = {'i': 4, 'd': 8, 'f':4}
  58. class ChunkFile(object):
  59. '''
  60. classdocs
  61. '''
  62. def __init__(self, file_path=None):
  63. '''
  64. Constructor
  65. '''
  66. self.file_path = file_path
  67. self.file = None
  68. self.chunk_names = []
  69. self.chunk_positions = {}
  70. self.version = (0,0)
  71. if self.file_path is not None:
  72. self.open(file_path)
  73. def open(self, file_path):
  74. self.file = open(file_path, "rb")
  75. if self.file is None:
  76. raise Exception("konnte Datei nicht oeffnen")
  77. self.read_header(self.file)
  78. self.chunk_positions = self.read_chunk_headers(self.file)
  79. def close(self):
  80. if self.file is not None:
  81. self.file.close()
  82. @staticmethod
  83. def read_chunk_headers(file_obj):
  84. chunk_positions = {}
  85. try:
  86. while True:
  87. pos = file_obj.tell()
  88. chunk_header = ChunkFile.read_chunk_header(file_obj)
  89. chunk_positions[chunk_header.name] = (pos, chunk_header)
  90. file_obj.seek(pos + chunk_header.get_total_chunksize())
  91. except NoMoreChunksFound:
  92. pass
  93. return chunk_positions
  94. def read_header(self, file_obj):
  95. self.format_name = self.read_name_string(file_obj)
  96. self.minor, self.major = self.read_version(file_obj)
  97. def get_format(self):
  98. '''
  99. get file format string
  100. '''
  101. return self.format_name
  102. def get_chunk_names(self):
  103. return self.chunk_positions.keys()
  104. def get_version(self):
  105. return self.minor, self.major
  106. def read_chunk(self, chunk_name):
  107. if not chunk_name in self.chunk_positions:
  108. raise ChunkNotFound(chunk_name)
  109. pos, header = self.chunk_positions[chunk_name]
  110. self.file.seek(pos + header.get_header_size())
  111. data = self.file.read(header.get_data_size())
  112. return data
  113. def read_array(self, chunk_name, datatype='i'):
  114. if not chunk_name in self.chunk_positions:
  115. raise ChunkNotFound(chunk_name)
  116. pos, header = self.chunk_positions[chunk_name]
  117. self.file.seek(pos + header.get_header_size())
  118. if datatype_size_map[datatype] != header.element_size:
  119. raise WrongElementSize(str(header.element_size))
  120. data = numpy.fromfile(self.file, dtype=datatype, count=header.n_elements)
  121. return data
  122. @staticmethod
  123. def read_name_string(file_obj):
  124. s = file_obj.read(1)
  125. if len(s) == 0:
  126. raise NoChunkNameFound
  127. (length,) = struct.unpack('b', s)
  128. s = file_obj.read(length)
  129. if len(s) == 0:
  130. raise UnexpectedEndOfChunkFile
  131. return s
  132. @staticmethod
  133. def read_version(file_obj):
  134. minor, major = struct.unpack('ii', file_obj.read(8))
  135. logging.debug(str(minor) + " " + str(major))
  136. return minor, major
  137. @staticmethod
  138. def read_chunk_header(file_obj):
  139. try:
  140. chunk_name = ChunkFile.read_name_string(file_obj)
  141. logging.debug("chunk name = " + chunk_name)
  142. except NoChunkNameFound:
  143. raise NoMoreChunksFound
  144. s = file_obj.read(3*8)
  145. chunk_size, element_size, n_elements = struct.unpack('QQQ', s)
  146. logging.debug(str(chunk_size) + " " + str(element_size) + " " + str(n_elements))
  147. return ChunkHeader(chunk_name, chunk_size, element_size, n_elements)
  148. class ChunkFileWriter(object):
  149. def __init__(self, file_path, file_format):
  150. self.file_path = file_path
  151. self.myfile = open(file_path, 'wb')
  152. self.file_format = file_format
  153. self.file_format.write(self.myfile)
  154. def write_byte_chunk(self, name, data):
  155. data_size = len(data)
  156. chunk_size = data_size + 2*8 # 2*sizeof(size_t) auf x64 System
  157. ch = ChunkHeader(name, chunk_size, data_size, 1)
  158. ch.write(self.myfile)
  159. self.myfile.write(struct.pack(str(data_size) + 's', data))
  160. def write_int_array_chunk(self, name, int_array):
  161. element_size = datatype_size_map['i']
  162. n_elements = len(int_array)
  163. chunk_size = n_elements*element_size + 2*8
  164. ch = ChunkHeader(name, chunk_size, element_size, n_elements)
  165. ch.write(self.myfile)
  166. int_array.tofile(self.myfile, format='%i')
  167. def write_float_array_chunk(self, name, float_array):
  168. element_size = datatype_size_map['f']
  169. n_elements = len(float_array)
  170. chunk_size = n_elements*element_size + 2*8
  171. ch = ChunkHeader(name, chunk_size, element_size, n_elements)
  172. ch.write(self.myfile)
  173. float_array.tofile(self.myfile, format='%f')
  174. def close(self):
  175. self.myfile.close()