Removes the PHI from the supplied twix header and returns the sanitized
    version. This consists of:
    1) Replacing the patient id and name with strings of lower case x
    characters.
    2) Replacing the patient birthday with 19700101
    3) Replacing the patient gender with the number 0
    4) All references to the date and time of the exam have all alphanumeric
    characters replaced by lower case x. Other characters (notably the period)
    are left unchanged.

    :param header_string: The header string to be anonymized
    :return: The anonymized version of the header.

def anonymize_twix_header(header_string):
    """
    Removes the PHI from the supplied twix header and returns the sanitized
    version. This consists of:
    1) Replacing the patient id and name with strings of lower case x
    characters.
    2) Replacing the patient birthday with 19700101
    3) Replacing the patient gender with the number 0
    4) All references to the date and time of the exam have all alphanumeric
    characters replaced by lower case x. Other characters (notably the period)
    are left unchanged.

    :param header_string: The header string to be anonymized
    :return: The anonymized version of the header.
    """
    patient_id = re.search(r"(<ParamString.\"PatientID\">  { )(\".+\")(  }\n)", header_string).group(2)
    header_string = re.sub(patient_id, "\"" + ("x" * (len(patient_id) - 2)) + "\"", header_string)

    patient_birthday = re.search(r"(<ParamString.\"PatientBirthDay\">  { )(\".+\")(  }\n)", header_string).group(2)
    header_string = re.sub(patient_birthday, "\"19700101\"", header_string)

    patient_name = re.escape(re.search(r"(<ParamString.\"PatientName\">  { )(\".+\")(  }\n)", header_string).group(2))
    # every occurrence of patient_name, replace it with a string where all the
    # characters apart from the surrounding quotations are replaced with x
    header_string = re.sub(patient_name, lambda x: re.sub(r"[^\"]", "x", x.group()), header_string)

    patient_gender_span = re.search(r"(<ParamLong.\"PatientSex\">  { )(\d+)(  }\n)", header_string).span(2)
    header_string = header_string[:patient_gender_span[0]] + "0" + header_string[patient_gender_span[1]:]

    header_string = re.sub("(Sex\">\s+\{\s*)(\d)(\s*\})",
                           lambda match: "".join((match.group(1), "0", match.group(3))),
                           header_string)

    # We need to remove information which contains the date and time of the exam
    # this is not stored in a helpful way which complicates finding it.
    # I think that this FrameOfReference parameter is the correct time, it is
    # certainly the correct date.
    # As Siemens uses date and time to refer to other scans, we need to censor
    # any string which contains the date of this exam. Also some references
    # seem to use the date with the short form of the year so we match that
    frame_of_reference = re.search(r"(<ParamString.\"FrameOfReference\">  { )(\".+\")(  }\n)", header_string).group(2)
    exam_date_time = frame_of_reference.split(".")[10]
    exam_date = exam_date_time[2:8]

    # any string which contains the exam date has all alpha-numerics replaced
    # by the character x
    header_string = re.sub(r"\"[\d\.]*{0}[\d\.]*\"".format(exam_date),
                           lambda match: re.sub(r"\w", "x", match.group()),
                           header_string)

    return header_string