the incident record the re multiline flag means that in this regular expression matches at the start of every line (rather than just at the start of the string)and matches at the end of every line (rather than just at the end of the string)so the narrative start and end markers are matched only at the start of lines the second regular expressionkey_value_reis used to capture key=value linesand it matches at the start of every line in the text it is given to match againstwhere the line begins with any amount of whitespace (including none)followed by non-characters which are captured into the key match groupfollowed by an characterfollowed by all the remaining characters in the line (excluding any leading or trailing whitespace)and captures them into the value match group the fundamental logic used to parse the file is the same as we used for the manual text parser that we covered in the previous subsectiononly this time we extract incident records and incident data within those records using regular expressions rather than reading line by line fh none tryfh open(filenameencoding="utf "self clear(for incident_match in incident_re finditer(fh read())data {data["report_id"incident_match group("id"data["narrative"textwrap dedentincident_match group("narrative")strip(keyvalues incident_match group("keyvalues"for match in key_value_re finditer(keyvalues)data[match group("key")match group("value"data["date"datetime datetime strptimedata["date"]"% -% -% "date(data["pilot_percent_hours_on_type"float(data["pilot_percent_hours_on_type"])data["pilot_total_hours"intdata["pilot_total_hours"]data["midair"bool(int(data["midair"])if len(data! raise incidenterror("missing data"incident incident(**dataself[incident report_idincident return true the re finditer(method returns an iterator which produces each nonoverlapping match in turn we create data dictionary to hold one incident' data as we have done beforebut this time we get the report id and narrative text