""" This module is a trivial data file parser. It allows one to create commented text data files and be able to read them from Python. If you have a file like so: ---- Start of file: test.dat --- # This is a comment velocity = 1.0, angle = 45 (degrees) n = 2 # Another comment. x = 0.1, y = 0.1 x = 0.2, y = 0.2 ---- EOF --- Then you can do the following: p = DataParser() p.parse_file('test.dat') v = p.get_float() a = p.get_float() n = p.get_int() for i in range(2): x, y = p.get_float(), p.get_float() The parser supports more than just floats and ints. The test at the bottom of the file shows you pretty much all that is possible. UTSL. Bugs: There are possibly better ways to implement this but it works well enough for me. License: Python License. Author: Prabhu Ramachandran """ import re, string, types class DataError(Exception): pass class DataParser: def __init__(self): self.comment = re.compile('#.*\n?') self.white = re.compile('[,;:=\(\)]+') def strip_comments(self, data): data = self.comment.sub(' ', data) return self.white.sub(' ', data) def parse_file(self, file): """The argument 'file' can be either a valid file name or an object that has a read() method.""" if type(file) is types.StringType: f = open(file) elif hasattr(file, 'read'): f = file else: msg = "Need to pass a filename or file like object "\ "that has a read() method." raise DataError, msg self.parse_data(f.read()) f.close() def parse_data(self, data): data = self.strip_comments(data) self.words = string.split(data) self.words.reverse() def get_val(self, converter=float): while self.words: w = self.words.pop() try: val = converter(w) except ValueError: pass else: return val raise DataError, "Insufficient data in stream" def get_float(self): return self.get_val(float) def get_int(self): return self.get_val(int) def get_string(self): if self.words: return self.words.pop() else: raise DataError, "Insufficient data in stream" def get_alpha(self): while self.words: w = self.words.pop() if len(w) == 1: if w in string.letters: return w raise DataError, "Insufficient data in stream" def get_alnum(self): while self.words: w = self.words.pop() if len(w) == 1: if w in string.letters or w in string.digits: return w raise DataError, "Insufficient data in stream" def get_complex(self): re = self.get_float() im = self.get_float() return (re, im) def write_test_data(): import StringIO f = StringIO.StringIO() # comments f.write("# test comment\n1 # embedded comment\n1\n") # int, float, float exponent f.write("x = 1\ny = 1.123\ny = 1.01325e5\n") # complex f.write("z = 1, 1\n") # spacing with , ; : () = f.write("x=1.1,1.1;1.1:1.1(1.1)1.1\n") # string, alnum, alpha f.write("test_12\n1, x, a\n") f.seek(0) return f def test_parser(): f = write_test_data() p = DataParser() p.parse_file(f) # test comments assert p.get_int() == 1 assert p.get_int() == 1 # test int, float, float with exponent. assert p.get_int() == 1 assert p.get_float() == 1.123 assert p.get_float() == 1.01325e5 # test complex assert p.get_complex() == (1,1) # spacing with = , ; : () for i in '=,;:()': assert p.get_float() == 1.1 # string, alnum, aplha assert p.get_string() == 'test_12' assert p.get_alnum() == '1' assert p.get_alnum() == 'x' assert p.get_alpha() == 'a' if __name__ == "__main__": test_parser()