def detectXMLEncoding(fp):
    """ Attempts to detect the character encoding of the xml file
    given by a file object fp. fp must not be a codec wrapped file
    object!

    The return value can be:
        - if detection of the BOM succeeds, the codec name of the
        corresponding unicode charset is returned

        - if BOM detection fails, the xml declaration is searched for
        the encoding attribute and its value returned. the "<"
        character has to be the very first in the file then (it's xml
        standard after all).

        - if BOM and xml declaration fail, None is returned. According
        to xml 1.0 it should be utf_8 then, but it wasn't detected by
        the means offered here. at least one can be pretty sure that a
        character coding including most of ASCII is used :-/
    """
    bomDict = {(0, 0, 254, 255): 'utf_32_be', (255, 254, 0, 0): 'utf_32_le',
        (254, 255, None, None): 'utf_16_be', (255, 254, None, None):
        'utf_16_le', (239, 187, 191, None): 'utf_8'}
    oldFP = fp.tell()
    fp.seek(0)
    byte1, byte2, byte3, byte4 = tuple(map(ord, fp.read(4)))
    bomDetection = bomDict.get((byte1, byte2, byte3, byte4))
    if not bomDetection:
        bomDetection = bomDict.get((byte1, byte2, byte3, None))
        if not bomDetection:
            bomDetection = bomDict.get((byte1, byte2, None, None))
    if bomDetection:
        fp.seek(oldFP)
        return bomDetection
    import re
    fp.seek(0)
    buffer = fp.read(2048)
    xmlDeclPattern = """
    ^<\\?xml             # w/o BOM, xmldecl starts with <?xml at the first byte
    .+?                 # some chars (version info), matched minimal
    encoding=           # encoding attribute begins
    ["']                # attribute start delimiter
    (?P<encstr>         # what's matched in the brackets will be named encstr
     [^"']+              # every character not delimiter (not overly exact!)
    )                   # closes the brackets pair for the named group
    ["']                # attribute end delimiter
    .*?                 # some chars optionally (standalone decl or whitespace)
    \\?>                 # xmldecl end
    """
    xmlDeclRE = re.compile(xmlDeclPattern, re.VERBOSE)
    match = xmlDeclRE.search(buffer)
    fp.seek(oldFP)
    if match:
        return match.group('encstr')
    else:
        return None
