def guess_codec(file, errors="strict", require_char=False):
    """Look at file contents and guess its correct encoding.

    File must be open in binary mode and positioned at offset 0. If BOM
    record is present then it is assumed to be UTF-8 or UTF-16 encoded
    file. GEDCOM header is searched for CHAR record and encoding name
    is extracted from it, if BOM record is present then CHAR record
    must match BOM-defined encoding.

    :param file: File object, must be open in binary mode.
    :param str errors: Controls error handling behavior during string
        decoding, accepts same values as standard `codecs.decode` method.
    :param bool require_char: If True then exception is thrown if CHAR
        record is not found in a header, if False and CHAR is not in the
        header then codec determined from BOM or "gedcom" is returned.
    :returns: Tuple (codec_name, bom_size)
    :raises: :py:class:`CodecError` when codec name in file is unknown or
        when codec name in file contradicts codec determined from BOM.
    :raises: :py:class:`UnicodeDecodeError` when codec fails to decode
        input lines and `errors` is set to "strict" (default).
    """

    # mapping of gedcom character set specifiers to Python encoding names
    gedcom_char_to_codec = {
        'ansel': 'gedcom',
    }

    # check BOM first
    bom_codec = check_bom(file)
    bom_size = file.tell()
    codec = bom_codec or 'gedcom'

    # scan header until CHAR or end of header
    while True:

        # this stops at '\n'
        line = file.readline()
        if not line:
            raise IOError("Unexpected EOF while reading GEDCOM header")

        # do not decode bytes to strings here, reason is that some
        # stupid apps split CONC record at byte level (in middle of
        # of multi-byte characters). This implies that we can only
        # work with encodings that have ASCII as single-byte subset.

        line = line.lstrip().rstrip(b"\r\n")
        words = line.split()

        if len(words) >= 2 and words[0] == b"0" and words[1] != b"HEAD":
            # past header but have not seen CHAR
            if require_char:
                raise CodecError("GEDCOM header does not have CHAR record")
            else:
                break
        elif len(words) >= 3 and words[0] == b"1" and words[1] == b"CHAR":
            try:
                encoding = words[2].decode(codec, errors)
                encoding = gedcom_char_to_codec.get(encoding.lower(),
                                                    encoding.lower())
                new_codec = codecs.lookup(encoding).name
            except LookupError:
                raise CodecError("Unknown codec name {0}".format(encoding))
            if bom_codec is None:
                codec = new_codec
            elif new_codec != bom_codec:
                raise CodecError("CHAR codec {0} is different from BOM "
                                 "codec {1}".format(new_codec, bom_codec))
            break

    return codec, bom_size
Look at file contents and guess its correct encoding.

    File must be open in binary mode and positioned at offset 0. If BOM
    record is present then it is assumed to be UTF-8 or UTF-16 encoded
    file. GEDCOM header is searched for CHAR record and encoding name
    is extracted from it, if BOM record is present then CHAR record
    must match BOM-defined encoding.

    :param file: File object, must be open in binary mode.
    :param str errors: Controls error handling behavior during string
        decoding, accepts same values as standard `codecs.decode` method.
    :param bool require_char: If True then exception is thrown if CHAR
        record is not found in a header, if False and CHAR is not in the
        header then codec determined from BOM or "gedcom" is returned.
    :returns: Tuple (codec_name, bom_size)
    :raises: :py:class:`CodecError` when codec name in file is unknown or
        when codec name in file contradicts codec determined from BOM.
    :raises: :py:class:`UnicodeDecodeError` when codec fails to decode
        input lines and `errors` is set to "strict" (default).