import re from typing import Tuple def readasc(filename : str) -> Tuple[dict, dict]: """ Reads Siemens ASC ascii-formatted textfile and returns a dictionary structure. E.g. a[0].b[2][3].c = "string" parses into: asc['a'][0]['b'][2][3]['c'] = "string" Parameters ---------- filename : str Filename of the ASC file. Returns ------- asc : dict Dictionary of ASC part of file. extra : dict Dictionary of other fields after "ASCCONV END" """ asc, extra = {}, {} # Read asc file and convert it into a dictionary structure with open(filename, 'r') as fp: end_of_asc = False for next_line in fp: next_line = next_line.strip() if next_line == '### ASCCONV END ###': # find end of mrProt in the asc file end_of_asc = True if next_line == '' or next_line[0] == '#': continue # regex wizardry: Matches lines like 'a[0].b[2][3].c = "string" # comment' # Note this assumes correct formatting, e.g. does not check whether # brackets match. match = re.match(r'^\s*([a-zA-Z0-9\[\]\._]+)\s*\=\s*(("[^"]*"|\'[^\']\')|(\d+)|([0-9\.e\-]+))\s*((#|\/\/)(.*))?$', next_line) if match: field_name = match[1] # Keep track of where to put the value: base[assign_to] = value if end_of_asc: base = extra else: base = asc assign_to = None # Iterate over every segment of the field name parts = field_name.split('.') for p in parts: # Update base so final assignement is like: base[assign_to][p] = value if assign_to != None and assign_to not in base: base[assign_to] = {} if assign_to != None: base = base[assign_to] # Iterate over brackets start = p.find('[') if start != -1: name = p[:start] assign_to = name while start != -1: stop = p.find(']', start) index = int(p[start+1:stop]) # Update base so final assignement is like: base[assign_to][p][index] = value if assign_to not in base: base[assign_to] = {} base = base[assign_to] assign_to = index start = p.find('[', stop) else: assign_to = p # Depending on which regex section matched we can infer the value type if match[3]: base[assign_to] = match[3][1:-1] elif match[4]: base[assign_to] = int(match[4]) elif match[5]: base[assign_to] = float(match[5]) else: raise RuntimeError('This should not be reached') elif next_line.find('=') != -1: raise RuntimeError(f'Bug: ASC line with an assignment was not parsed correctly: {next_line}') return asc, extra