readasc.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import re
  2. from typing import Tuple
  3. def readasc(filename : str) -> Tuple[dict, dict]:
  4. """
  5. Reads Siemens ASC ascii-formatted textfile and returns a dictionary
  6. structure.
  7. E.g. a[0].b[2][3].c = "string"
  8. parses into:
  9. asc['a'][0]['b'][2][3]['c'] = "string"
  10. Parameters
  11. ----------
  12. filename : str
  13. Filename of the ASC file.
  14. Returns
  15. -------
  16. asc : dict
  17. Dictionary of ASC part of file.
  18. extra : dict
  19. Dictionary of other fields after "ASCCONV END"
  20. """
  21. asc, extra = {}, {}
  22. # Read asc file and convert it into a dictionary structure
  23. with open(filename, 'r') as fp:
  24. end_of_asc = False
  25. for next_line in fp:
  26. next_line = next_line.strip()
  27. if next_line == '### ASCCONV END ###': # find end of mrProt in the asc file
  28. end_of_asc = True
  29. if next_line == '' or next_line[0] == '#':
  30. continue
  31. # regex wizardry: Matches lines like 'a[0].b[2][3].c = "string" # comment'
  32. # Note this assumes correct formatting, e.g. does not check whether
  33. # brackets match.
  34. match = re.match(r'^\s*([a-zA-Z0-9\[\]\._]+)\s*\=\s*(("[^"]*"|\'[^\']\')|(\d+)|([0-9\.e\-]+))\s*((#|\/\/)(.*))?$', next_line)
  35. if match:
  36. field_name = match[1]
  37. # Keep track of where to put the value: base[assign_to] = value
  38. if end_of_asc:
  39. base = extra
  40. else:
  41. base = asc
  42. assign_to = None
  43. # Iterate over every segment of the field name
  44. parts = field_name.split('.')
  45. for p in parts:
  46. # Update base so final assignement is like: base[assign_to][p] = value
  47. if assign_to != None and assign_to not in base:
  48. base[assign_to] = {}
  49. if assign_to != None:
  50. base = base[assign_to]
  51. # Iterate over brackets
  52. start = p.find('[')
  53. if start != -1:
  54. name = p[:start]
  55. assign_to = name
  56. while start != -1:
  57. stop = p.find(']', start)
  58. index = int(p[start+1:stop])
  59. # Update base so final assignement is like: base[assign_to][p][index] = value
  60. if assign_to not in base:
  61. base[assign_to] = {}
  62. base = base[assign_to]
  63. assign_to = index
  64. start = p.find('[', stop)
  65. else:
  66. assign_to = p
  67. # Depending on which regex section matched we can infer the value type
  68. if match[3]:
  69. base[assign_to] = match[3][1:-1]
  70. elif match[4]:
  71. base[assign_to] = int(match[4])
  72. elif match[5]:
  73. base[assign_to] = float(match[5])
  74. else:
  75. raise RuntimeError('This should not be reached')
  76. elif next_line.find('=') != -1:
  77. raise RuntimeError(f'Bug: ASC line with an assignment was not parsed correctly: {next_line}')
  78. return asc, extra