code: def read_pattern(text_str, patterns, terminate_on_match=False,
                   postprocess=str):
      
  
      compiled = {
          key: re.compile(pattern, re.MULTILINE | re.DOTALL)
          for key, pattern in patterns.items()
      }
      matches = defaultdict(list)
      for key, pattern in compiled.items():
          for match in pattern.finditer(text_str):
              matches[key].append([postprocess(i) for i in match.groups()])
              if terminate_on_match:
                  break
      return matches
docs: General pattern reading on an input string
  
          Args:
              text_str (str): the input string to search for patterns
              patterns (dict): A dict of patterns, e.g.,
                  {"energy": r"energy\\(sigma->0\\)\\s+=\\s+([\\d\\-.]+)"}.
              terminate_on_match (bool): Whether to terminate when there is at
                  least one match in each key in pattern.
              postprocess (callable): A post processing function to convert all
                  matches. Defaults to str, i.e., no change.
  
          Renders accessible:
              Any attribute in patterns. For example,
              {"energy": r"energy\\(sigma->0\\)\\s+=\\s+([\\d\\-.]+)"} will set the
              value of matches["energy"] = [[-1234], [-3453], ...], to the
              results from regex and postprocess. Note that the returned values
              are lists of lists, because you can grep multiple items on one line.