# coding=utf-8
import re


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        # print(filter_text)
        # print('头文件读入，正在处理正则---> ' + filepath)
        apis = extract(filter_text)
        return apis
    return []


"""
1.读入内存，取出头文件文本内容
2.过滤注释 // /**/
3.过滤@protocol xxx, xxx; 这种特殊字符串
4.取出@interface和@protocol两块内容
5.匹配带参或者不带参的Method ClassName，Type，组成{}返回
6.删除interface和protocol两大块
7.匹配c方法 {'type': 'C/C++', 'ctype': ['Coretext']}
"""


def extract(text):
    # print(text)
    no_comment_text = remove_comment(text)
    # print(no_comment_text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


# 移除注释 // MULTILINE 模式  /* */ DOTALL模式
def remove_comment(text):
    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ""
        else:
            return s

    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        # EKCalendarChooser 该字符串是有换行的
        # 有参数方法  re.DOTALL 针对换行的写法
        method = re.compile("([+-] \([ *\w]*\).*?;)\s*", re.DOTALL)
        # 去参数 提取方法
        method_args = re.compile("(\w+:)")

        # 无参数方法提取  ?!的例子如下
        #  a = re.compile(r'windows(?!7|8|9|10|xp)')
        #  x = a.match('windows10') 后缀除了上面几个，才会匹配到windows，而且不会作为值进行group捕获
        method_no_args = re.compile("[+-] \([\w *]+\)\s*(\w+)(?!:)")
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append("".join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])

        return temp

    # 移除@protocol NSObject, OS_voucher;这种格式，避免后面的interface和protocol获取域不对
    remove_pro = re.compile("@protocol [\w ,]*;", )
    text = re.sub(remove_pro, "", text)
    # print(text)
    # interface 域
    interface = r"""
            @interface\s*
            .*?
            @end
        """
    # protocol 域
    protocol = r"""
            @protocol\s*
            .*?
            @end
        """
    # 获取classname正则
    class_name = re.compile("@interface\s*([\s\(\)\w]*)")
    # 获取interface正则编译
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    # finditer查找匹配迭代器 匹配到一组或者多组@interface @end
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    # print(classes)
    for c in classes:
        # 搜索到类名
        cm = class_name.search(c)
        if cm:
            # 类名trim
            cn = cm.groups()[0].replace(" ", "")
            cn = cn.strip()
        # 获取方法
        # print(c)
        temp = _get_methods(c)
        if temp:
            methods.append({"class": cn, "methods": temp, "type": "interface"})

    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    # 协议方法 @protocol OS_dispatch_queue;
    valid_protocol = re.compile("@protocol\s*[\w, ]*;")
    # 协议名 @protocol NSDecimalNumberBehaviors
    protocol_name = re.compile("@protocol\s*(\w*)")
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            # 这种是要移除的 最上面已经做了移除
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(" ", "")
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({"class": pn, "methods": temp, "type": "protocol"})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = r"""
            @interface\s*
            .*?
            @end
            |
            @protocol\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, "", text)


def get_c_func(text):
    """
    获取c方法
    :param text:
    :return:[{'type': 'C/C++', 'ctype': ['Coretext']}]
    """
    # delete struct and enum declariations
    del_struct_enum = r"""
        (?:typedef)?\s*struct\s*\w+\s*{[^}]*}\s*\w*;   # struct
        |
        \s*enum\s*{[^}]*\s*};  # enum
        |
        \s*{[^{}]*\s*}     #  function implement
    """
    del_regex = re.compile(del_struct_enum, re.VERBOSE | re.MULTILINE | re.DOTALL)
    text = re.sub(del_regex, "", text)
    pattern = r"""
        #^(?!\#)(?!\s*typedef\s\w+).*?(\w+)\s*\((?!\w+,)(?![^()*^]*(?:\d+_\d+|NA))
        ^(?!\#)(?!\s*typedef\s\w+).*?(\w+)\s*\((?!\w+,)(?!\d+_\d+|NA,)
        |
        \([*^]([^)]+)\)\s*\(
        |
        \#define\s*(\w+)\s?\(\s*\w*
    """
    # regex = re.compile(pattern)
    regex = re.compile(pattern, re.VERBOSE | re.MULTILINE)
    # regex = re.compile(pattern, re.VERBOSE|re.MULTILINE|re.DOTALL)
    # regex = re.compile(pattern, re.MULTILINE)
    # method = [m.group(1) for m in regex.finditer(text) if m.group(1)]
    # method = [m.group(0) for m in regex.finditer(text) if m.group(0)]
    # method = re.findall(regex, text)
    method = []
    for mm in regex.finditer(text):
        m = mm.groups()
        if m[0]:
            method.append(m[0])
        elif m[1]:
            method.append(m[1])
        elif m[2]:
            method.append(m[2])
    s = set(method)
    method = list(s)
    if len(method) > 0:
        return [{"class": "ctype", "methods": method, "type": "C/C++"}]
    else:
        return []


if __name__ == '__main__':
    """
    group介绍
    pattern = re.compile('(\d+)abc') 
    result = re.search(pattern, 'dsfdfdgfd123abcsdfdgfgf')
    result.group()
    '123abc'
    result.group(0)
    '123abc'
    result.group(1)
    '123'
    result.groups()[0]
    '123' 
    """

    """
    findall() 和 finditer()
    pat = re.compile('(\d+)abc')
    res = re.findall(pat, 'dssdfs1abcdsds123123abcsfdgfgf') 
    res.groups()
    error: 'list' object has no attribute 'groups'
 
    for x in res: 
		print(x)                                                                       
    1
    123123

    res = re.finditer(pat, 'dssdfs1abcdsds123123abcsfdgfgf')
    for x in res: 
        print(x) 
                                                                         
    <re.Match object; span=(6, 10), match='1abc'>
    <re.Match object; span=(14, 23), match='123123abc'>
    """

    # 乱码案例
    # class_info = get_apis_from_header_file(
    # '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/PDFKit.framework/Headers/PDFAnnotation.h')

    # 换行案例  EKCalendarChooser
    # class_info = get_apis_from_header_file(
    #     '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/EventKitUI.framework/Headers/EKCalendarChooser.h')

    class_info = get_apis_from_header_file('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/ARKit.framework/Headers/ARAnchor.h')

    print(class_info)

b8474e066735fea7f2b30e5aac41baf1f01442ff
<mask token>


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        apis = extract(filter_text)
        return apis
    return []


<mask token>


def extract(text):
    no_comment_text = remove_comment(text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


def remove_comment(text):

    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ''
        else:
            return s
    pattern = re.compile(
        '//.*?$|/\\*.*?\\*/|\\\'(?:\\\\.|[^\\\\\\\'])*\\\'|"(?:\\\\.|[^\\\\"])*"'
        , re.DOTALL | re.MULTILINE)
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        method = re.compile('([+-] \\([ *\\w]*\\).*?;)\\s*', re.DOTALL)
        method_args = re.compile('(\\w+:)')
        method_no_args = re.compile('[+-] \\([\\w *]+\\)\\s*(\\w+)(?!:)')
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append(''.join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])
        return temp
    remove_pro = re.compile('@protocol [\\w ,]*;')
    text = re.sub(remove_pro, '', text)
    interface = """
            @interface\\s*
            .*?
            @end
        """
    protocol = """
            @protocol\\s*
            .*?
            @end
        """
    class_name = re.compile('@interface\\s*([\\s\\(\\)\\w]*)')
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    for c in classes:
        cm = class_name.search(c)
        if cm:
            cn = cm.groups()[0].replace(' ', '')
            cn = cn.strip()
        temp = _get_methods(c)
        if temp:
            methods.append({'class': cn, 'methods': temp, 'type': 'interface'})
    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    valid_protocol = re.compile('@protocol\\s*[\\w, ]*;')
    protocol_name = re.compile('@protocol\\s*(\\w*)')
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(' ', '')
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({'class': pn, 'methods': temp, 'type':
                    'protocol'})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = """
            @interface\\s*
            .*?
            @end
            |
            @protocol\\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, '', text)


<mask token>

<mask token>


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        apis = extract(filter_text)
        return apis
    return []


<mask token>


def extract(text):
    no_comment_text = remove_comment(text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


def remove_comment(text):

    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ''
        else:
            return s
    pattern = re.compile(
        '//.*?$|/\\*.*?\\*/|\\\'(?:\\\\.|[^\\\\\\\'])*\\\'|"(?:\\\\.|[^\\\\"])*"'
        , re.DOTALL | re.MULTILINE)
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        method = re.compile('([+-] \\([ *\\w]*\\).*?;)\\s*', re.DOTALL)
        method_args = re.compile('(\\w+:)')
        method_no_args = re.compile('[+-] \\([\\w *]+\\)\\s*(\\w+)(?!:)')
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append(''.join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])
        return temp
    remove_pro = re.compile('@protocol [\\w ,]*;')
    text = re.sub(remove_pro, '', text)
    interface = """
            @interface\\s*
            .*?
            @end
        """
    protocol = """
            @protocol\\s*
            .*?
            @end
        """
    class_name = re.compile('@interface\\s*([\\s\\(\\)\\w]*)')
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    for c in classes:
        cm = class_name.search(c)
        if cm:
            cn = cm.groups()[0].replace(' ', '')
            cn = cn.strip()
        temp = _get_methods(c)
        if temp:
            methods.append({'class': cn, 'methods': temp, 'type': 'interface'})
    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    valid_protocol = re.compile('@protocol\\s*[\\w, ]*;')
    protocol_name = re.compile('@protocol\\s*(\\w*)')
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(' ', '')
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({'class': pn, 'methods': temp, 'type':
                    'protocol'})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = """
            @interface\\s*
            .*?
            @end
            |
            @protocol\\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, '', text)


def get_c_func(text):
    """
    获取c方法
    :param text:
    :return:[{'type': 'C/C++', 'ctype': ['Coretext']}]
    """
    del_struct_enum = """
        (?:typedef)?\\s*struct\\s*\\w+\\s*{[^}]*}\\s*\\w*;   # struct
        |
        \\s*enum\\s*{[^}]*\\s*};  # enum
        |
        \\s*{[^{}]*\\s*}     #  function implement
    """
    del_regex = re.compile(del_struct_enum, re.VERBOSE | re.MULTILINE | re.
        DOTALL)
    text = re.sub(del_regex, '', text)
    pattern = """
        #^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?![^()*^]*(?:\\d+_\\d+|NA))
        ^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?!\\d+_\\d+|NA,)
        |
        \\([*^]([^)]+)\\)\\s*\\(
        |
        \\#define\\s*(\\w+)\\s?\\(\\s*\\w*
    """
    regex = re.compile(pattern, re.VERBOSE | re.MULTILINE)
    method = []
    for mm in regex.finditer(text):
        m = mm.groups()
        if m[0]:
            method.append(m[0])
        elif m[1]:
            method.append(m[1])
        elif m[2]:
            method.append(m[2])
    s = set(method)
    method = list(s)
    if len(method) > 0:
        return [{'class': 'ctype', 'methods': method, 'type': 'C/C++'}]
    else:
        return []


<mask token>

<mask token>


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        apis = extract(filter_text)
        return apis
    return []


<mask token>


def extract(text):
    no_comment_text = remove_comment(text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


def remove_comment(text):

    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ''
        else:
            return s
    pattern = re.compile(
        '//.*?$|/\\*.*?\\*/|\\\'(?:\\\\.|[^\\\\\\\'])*\\\'|"(?:\\\\.|[^\\\\"])*"'
        , re.DOTALL | re.MULTILINE)
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        method = re.compile('([+-] \\([ *\\w]*\\).*?;)\\s*', re.DOTALL)
        method_args = re.compile('(\\w+:)')
        method_no_args = re.compile('[+-] \\([\\w *]+\\)\\s*(\\w+)(?!:)')
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append(''.join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])
        return temp
    remove_pro = re.compile('@protocol [\\w ,]*;')
    text = re.sub(remove_pro, '', text)
    interface = """
            @interface\\s*
            .*?
            @end
        """
    protocol = """
            @protocol\\s*
            .*?
            @end
        """
    class_name = re.compile('@interface\\s*([\\s\\(\\)\\w]*)')
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    for c in classes:
        cm = class_name.search(c)
        if cm:
            cn = cm.groups()[0].replace(' ', '')
            cn = cn.strip()
        temp = _get_methods(c)
        if temp:
            methods.append({'class': cn, 'methods': temp, 'type': 'interface'})
    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    valid_protocol = re.compile('@protocol\\s*[\\w, ]*;')
    protocol_name = re.compile('@protocol\\s*(\\w*)')
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(' ', '')
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({'class': pn, 'methods': temp, 'type':
                    'protocol'})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = """
            @interface\\s*
            .*?
            @end
            |
            @protocol\\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, '', text)


def get_c_func(text):
    """
    获取c方法
    :param text:
    :return:[{'type': 'C/C++', 'ctype': ['Coretext']}]
    """
    del_struct_enum = """
        (?:typedef)?\\s*struct\\s*\\w+\\s*{[^}]*}\\s*\\w*;   # struct
        |
        \\s*enum\\s*{[^}]*\\s*};  # enum
        |
        \\s*{[^{}]*\\s*}     #  function implement
    """
    del_regex = re.compile(del_struct_enum, re.VERBOSE | re.MULTILINE | re.
        DOTALL)
    text = re.sub(del_regex, '', text)
    pattern = """
        #^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?![^()*^]*(?:\\d+_\\d+|NA))
        ^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?!\\d+_\\d+|NA,)
        |
        \\([*^]([^)]+)\\)\\s*\\(
        |
        \\#define\\s*(\\w+)\\s?\\(\\s*\\w*
    """
    regex = re.compile(pattern, re.VERBOSE | re.MULTILINE)
    method = []
    for mm in regex.finditer(text):
        m = mm.groups()
        if m[0]:
            method.append(m[0])
        elif m[1]:
            method.append(m[1])
        elif m[2]:
            method.append(m[2])
    s = set(method)
    method = list(s)
    if len(method) > 0:
        return [{'class': 'ctype', 'methods': method, 'type': 'C/C++'}]
    else:
        return []


if __name__ == '__main__':
    """
    group介绍
    pattern = re.compile('(\\d+)abc') 
    result = re.search(pattern, 'dsfdfdgfd123abcsdfdgfgf')
    result.group()
    '123abc'
    result.group(0)
    '123abc'
    result.group(1)
    '123'
    result.groups()[0]
    '123' 
    """
    """
    findall() 和 finditer()
    pat = re.compile('(\\d+)abc')
    res = re.findall(pat, 'dssdfs1abcdsds123123abcsfdgfgf') 
    res.groups()
    error: 'list' object has no attribute 'groups'
 
    for x in res: 
		print(x)                                                                       
    1
    123123

    res = re.finditer(pat, 'dssdfs1abcdsds123123abcsfdgfgf')
    for x in res: 
        print(x) 
                                                                         
    <re.Match object; span=(6, 10), match='1abc'>
    <re.Match object; span=(14, 23), match='123123abc'>
    """
    class_info = get_apis_from_header_file(
        '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/ARKit.framework/Headers/ARAnchor.h'
        )
    print(class_info)

import re


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        apis = extract(filter_text)
        return apis
    return []


<mask token>


def extract(text):
    no_comment_text = remove_comment(text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


def remove_comment(text):

    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ''
        else:
            return s
    pattern = re.compile(
        '//.*?$|/\\*.*?\\*/|\\\'(?:\\\\.|[^\\\\\\\'])*\\\'|"(?:\\\\.|[^\\\\"])*"'
        , re.DOTALL | re.MULTILINE)
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        method = re.compile('([+-] \\([ *\\w]*\\).*?;)\\s*', re.DOTALL)
        method_args = re.compile('(\\w+:)')
        method_no_args = re.compile('[+-] \\([\\w *]+\\)\\s*(\\w+)(?!:)')
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append(''.join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])
        return temp
    remove_pro = re.compile('@protocol [\\w ,]*;')
    text = re.sub(remove_pro, '', text)
    interface = """
            @interface\\s*
            .*?
            @end
        """
    protocol = """
            @protocol\\s*
            .*?
            @end
        """
    class_name = re.compile('@interface\\s*([\\s\\(\\)\\w]*)')
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    for c in classes:
        cm = class_name.search(c)
        if cm:
            cn = cm.groups()[0].replace(' ', '')
            cn = cn.strip()
        temp = _get_methods(c)
        if temp:
            methods.append({'class': cn, 'methods': temp, 'type': 'interface'})
    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    valid_protocol = re.compile('@protocol\\s*[\\w, ]*;')
    protocol_name = re.compile('@protocol\\s*(\\w*)')
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(' ', '')
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({'class': pn, 'methods': temp, 'type':
                    'protocol'})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = """
            @interface\\s*
            .*?
            @end
            |
            @protocol\\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, '', text)


def get_c_func(text):
    """
    获取c方法
    :param text:
    :return:[{'type': 'C/C++', 'ctype': ['Coretext']}]
    """
    del_struct_enum = """
        (?:typedef)?\\s*struct\\s*\\w+\\s*{[^}]*}\\s*\\w*;   # struct
        |
        \\s*enum\\s*{[^}]*\\s*};  # enum
        |
        \\s*{[^{}]*\\s*}     #  function implement
    """
    del_regex = re.compile(del_struct_enum, re.VERBOSE | re.MULTILINE | re.
        DOTALL)
    text = re.sub(del_regex, '', text)
    pattern = """
        #^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?![^()*^]*(?:\\d+_\\d+|NA))
        ^(?!\\#)(?!\\s*typedef\\s\\w+).*?(\\w+)\\s*\\((?!\\w+,)(?!\\d+_\\d+|NA,)
        |
        \\([*^]([^)]+)\\)\\s*\\(
        |
        \\#define\\s*(\\w+)\\s?\\(\\s*\\w*
    """
    regex = re.compile(pattern, re.VERBOSE | re.MULTILINE)
    method = []
    for mm in regex.finditer(text):
        m = mm.groups()
        if m[0]:
            method.append(m[0])
        elif m[1]:
            method.append(m[1])
        elif m[2]:
            method.append(m[2])
    s = set(method)
    method = list(s)
    if len(method) > 0:
        return [{'class': 'ctype', 'methods': method, 'type': 'C/C++'}]
    else:
        return []


if __name__ == '__main__':
    """
    group介绍
    pattern = re.compile('(\\d+)abc') 
    result = re.search(pattern, 'dsfdfdgfd123abcsdfdgfgf')
    result.group()
    '123abc'
    result.group(0)
    '123abc'
    result.group(1)
    '123'
    result.groups()[0]
    '123' 
    """
    """
    findall() 和 finditer()
    pat = re.compile('(\\d+)abc')
    res = re.findall(pat, 'dssdfs1abcdsds123123abcsfdgfgf') 
    res.groups()
    error: 'list' object has no attribute 'groups'
 
    for x in res: 
		print(x)                                                                       
    1
    123123

    res = re.finditer(pat, 'dssdfs1abcdsds123123abcsfdgfgf')
    for x in res: 
        print(x) 
                                                                         
    <re.Match object; span=(6, 10), match='1abc'>
    <re.Match object; span=(14, 23), match='123123abc'>
    """
    class_info = get_apis_from_header_file(
        '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/ARKit.framework/Headers/ARAnchor.h'
        )
    print(class_info)

# coding=utf-8
import re


def get_apis_from_header_file(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        # print(filter_text)
        # print('头文件读入，正在处理正则---> ' + filepath)
        apis = extract(filter_text)
        return apis
    return []


"""
1.读入内存，取出头文件文本内容
2.过滤注释 // /**/
3.过滤@protocol xxx, xxx; 这种特殊字符串
4.取出@interface和@protocol两块内容
5.匹配带参或者不带参的Method ClassName，Type，组成{}返回
6.删除interface和protocol两大块
7.匹配c方法 {'type': 'C/C++', 'ctype': ['Coretext']}
"""


def extract(text):
    # print(text)
    no_comment_text = remove_comment(text)
    # print(no_comment_text)
    method = []
    method += get_objc_func(no_comment_text)
    no_class = remove_objc(no_comment_text)
    method += get_c_func(no_class)
    return method


# 移除注释 // MULTILINE 模式  /* */ DOTALL模式
def remove_comment(text):
    def handler(match):
        s = match.group(0)
        if s.startswith('/'):
            return ""
        else:
            return s

    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, handler, text)


def get_objc_func(text):
    """
    获取objc的方法
    :param no_comment_text 移除评论的文本:
    :return: [{"":"","":"","":""}]
    """

    def _get_methods(text):
        """
        有参数
        + (void)_performExpiringActivityWithReason:(id)arg1 usingBlock:(CDUnknownBlockType)arg2;
        ['_performExpiringActivityWithReason:', 'usingBlock:']

        无参数
        + (void)_expireAllActivies;
        []

        ['_performExpiringActivityWithReason:usingBlock:',
         '_performActivityWithOptions:reason:usingBlock:',
         '_expireAllActivies',
         '_dumpExpiringActivitives',
         '_expiringActivities',
         '_expirationHandlerExecutionQueue',
         '_expiringTaskExecutionQueue',
         '_expiringAssertionManagementQueue',
         '_fireExpirationHandler',
         '_reactivate',
         '_end',
         'debugDescription',
         'dealloc',
         '_initWithActivityOptions:reason:expirationHandler:']
        :param text:
        :return: # 文件扫描结束后方法列表
        """
        # EKCalendarChooser 该字符串是有换行的
        # 有参数方法  re.DOTALL 针对换行的写法
        method = re.compile("([+-] \([ *\w]*\).*?;)\s*", re.DOTALL)
        # 去参数 提取方法
        method_args = re.compile("(\w+:)")

        # 无参数方法提取  ?!的例子如下
        #  a = re.compile(r'windows(?!7|8|9|10|xp)')
        #  x = a.match('windows10') 后缀除了上面几个，才会匹配到windows，而且不会作为值进行group捕获
        method_no_args = re.compile("[+-] \([\w *]+\)\s*(\w+)(?!:)")
        temp = []
        for m in method.finditer(text):
            if m:
                mline = m.groups()[0]
                args = re.findall(method_args, mline)
                if len(args) > 0:
                    temp.append("".join(args))
                else:
                    no_args = method_no_args.search(mline)
                    if no_args:
                        temp.append(no_args.groups()[0])

        return temp

    # 移除@protocol NSObject, OS_voucher;这种格式，避免后面的interface和protocol获取域不对
    remove_pro = re.compile("@protocol [\w ,]*;", )
    text = re.sub(remove_pro, "", text)
    # print(text)
    # interface 域
    interface = r"""
            @interface\s*
            .*?
            @end
        """
    # protocol 域
    protocol = r"""
            @protocol\s*
            .*?
            @end
        """
    # 获取classname正则
    class_name = re.compile("@interface\s*([\s\(\)\w]*)")
    # 获取interface正则编译
    inter_reg = re.compile(interface, re.VERBOSE | re.MULTILINE | re.DOTALL)
    methods = []
    # finditer查找匹配迭代器 匹配到一组或者多组@interface @end
    classes = [m.group(0) for m in inter_reg.finditer(text) if m.group(0)]
    # print(classes)
    for c in classes:
        # 搜索到类名
        cm = class_name.search(c)
        if cm:
            # 类名trim
            cn = cm.groups()[0].replace(" ", "")
            cn = cn.strip()
        # 获取方法
        # print(c)
        temp = _get_methods(c)
        if temp:
            methods.append({"class": cn, "methods": temp, "type": "interface"})

    protocol_reg = re.compile(protocol, re.VERBOSE | re.MULTILINE | re.DOTALL)
    protocols = [m.group(0) for m in protocol_reg.finditer(text) if m.group(0)]
    # 协议方法 @protocol OS_dispatch_queue;
    valid_protocol = re.compile("@protocol\s*[\w, ]*;")
    # 协议名 @protocol NSDecimalNumberBehaviors
    protocol_name = re.compile("@protocol\s*(\w*)")
    for p in protocols:
        valid = valid_protocol.search(p)
        if valid:
            # 这种是要移除的 最上面已经做了移除
            continue
        else:
            pm = protocol_name.search(p)
            if pm:
                pn = pm.groups()[0].replace(" ", "")
                pn = pn.strip()
            temp = _get_methods(p)
            if temp:
                methods.append({"class": pn, "methods": temp, "type": "protocol"})
    return methods


def remove_objc(text):
    """
    移除interface和protocol
    :param text:
    :return:
    """
    p = r"""
            @interface\s*
            .*?
            @end
            |
            @protocol\s*
            .*?
            @end
    """
    pattern = re.compile(p, re.VERBOSE | re.MULTILINE | re.DOTALL)
    return re.sub(pattern, "", text)


def get_c_func(text):
    """
    获取c方法
    :param text:
    :return:[{'type': 'C/C++', 'ctype': ['Coretext']}]
    """
    # delete struct and enum declariations
    del_struct_enum = r"""
        (?:typedef)?\s*struct\s*\w+\s*{[^}]*}\s*\w*;   # struct
        |
        \s*enum\s*{[^}]*\s*};  # enum
        |
        \s*{[^{}]*\s*}     #  function implement
    """
    del_regex = re.compile(del_struct_enum, re.VERBOSE | re.MULTILINE | re.DOTALL)
    text = re.sub(del_regex, "", text)
    pattern = r"""
        #^(?!\#)(?!\s*typedef\s\w+).*?(\w+)\s*\((?!\w+,)(?![^()*^]*(?:\d+_\d+|NA))
        ^(?!\#)(?!\s*typedef\s\w+).*?(\w+)\s*\((?!\w+,)(?!\d+_\d+|NA,)
        |
        \([*^]([^)]+)\)\s*\(
        |
        \#define\s*(\w+)\s?\(\s*\w*
    """
    # regex = re.compile(pattern)
    regex = re.compile(pattern, re.VERBOSE | re.MULTILINE)
    # regex = re.compile(pattern, re.VERBOSE|re.MULTILINE|re.DOTALL)
    # regex = re.compile(pattern, re.MULTILINE)
    # method = [m.group(1) for m in regex.finditer(text) if m.group(1)]
    # method = [m.group(0) for m in regex.finditer(text) if m.group(0)]
    # method = re.findall(regex, text)
    method = []
    for mm in regex.finditer(text):
        m = mm.groups()
        if m[0]:
            method.append(m[0])
        elif m[1]:
            method.append(m[1])
        elif m[2]:
            method.append(m[2])
    s = set(method)
    method = list(s)
    if len(method) > 0:
        return [{"class": "ctype", "methods": method, "type": "C/C++"}]
    else:
        return []


if __name__ == '__main__':
    """
    group介绍
    pattern = re.compile('(\d+)abc') 
    result = re.search(pattern, 'dsfdfdgfd123abcsdfdgfgf')
    result.group()
    '123abc'
    result.group(0)
    '123abc'
    result.group(1)
    '123'
    result.groups()[0]
    '123' 
    """

    """
    findall() 和 finditer()
    pat = re.compile('(\d+)abc')
    res = re.findall(pat, 'dssdfs1abcdsds123123abcsfdgfgf') 
    res.groups()
    error: 'list' object has no attribute 'groups'
 
    for x in res: 
		print(x)                                                                       
    1
    123123

    res = re.finditer(pat, 'dssdfs1abcdsds123123abcsfdgfgf')
    for x in res: 
        print(x) 
                                                                         
    <re.Match object; span=(6, 10), match='1abc'>
    <re.Match object; span=(14, 23), match='123123abc'>
    """

    # 乱码案例
    # class_info = get_apis_from_header_file(
    # '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/PDFKit.framework/Headers/PDFAnnotation.h')

    # 换行案例  EKCalendarChooser
    # class_info = get_apis_from_header_file(
    #     '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/EventKitUI.framework/Headers/EKCalendarChooser.h')

    class_info = get_apis_from_header_file('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/ARKit.framework/Headers/ARAnchor.h')

    print(class_info)
