基于 C 的 SQL Parser 实现

版权声明:原创文章,未经允许禁止转载! https://blog.csdn.net/mlibai/article/details/47726845

一个函数,可以提取SQL语句中查询字段部分。虽然函数中使用了block,但是block可以很方便的转换成纯 c 的函数,而且未来 block 也很有可能成为 c 标准。

#import <Foundation/Foundation.h>

typedef struct Field {
    char name[63];
    char orignal_name[63];
}Field;

const Field EmptyField = {'\0', '\0'};

static inline BOOL SameField (Field field1, Field field2) {
    if (strcmp(field1.name, field2.name) == 0 && strcmp(field1.orignal_name, field2.orignal_name) == 0) {
        return YES;
    }
    return NO;
};

void sqlParser(const char *sqlCString) {
    __block NSInteger sqlCStringLength = strlen(sqlCString);
    __block NSInteger i = 0;
    __block char *tmpCString = malloc(sizeof(char) * (sqlCStringLength + 1));
    
    // 从位置 i 处开始截取字符串,直到遇到结束符位置,截取的字符串包括起始位置和终止位置
    // 截取结束,i 指向的是所截取的字符串后面第一个字符。
    __block __weak void (^split_string_duplicate)(char, NSInteger);
    void (^split_string)(char, NSInteger) = ^(char endChar, NSInteger start){
        NSInteger j = start;
        BOOL meetEnd = NO;
        switch (endChar) {
            case ',':
                // 对于逗号,单独列出来
                tmpCString[j++] = sqlCString[i++];
                tmpCString[j] = '\0';
                break;
            case ')':
                // 以右括号结尾,要屏蔽单引号和反单引号里的字符串
                while (i < sqlCStringLength) {
                    // 因为字符数组的长度比字符数组元素的个数少1,这里不用担心越界的问题
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (meetEnd) {
                        tmpCString[j] = '\0';
                        break;
                    }
                    switch (sqlCString[i]) {
                        case '\'':
                            split_string_duplicate('\'', j);
                            j = strlen(tmpCString);
                            if (sqlCString[i] == ')') {
                                meetEnd = YES;
                            }
                            break;
                        case '`':
                            split_string_duplicate('`', j);
                            j = strlen(tmpCString);
                            if (sqlCString[i] == ')') {
                                meetEnd = YES;
                            }
                            break;
                        case ')':
                            meetEnd = YES;
                            break;
                        default:
                            break;
                    }
                }
                break;
            case '\'':
                // 单引号,要屏蔽两个连续的单引号
                while (i < sqlCStringLength) {
                    // 单引号的内容字符不转小写
                    tmpCString[j++] = sqlCString[i++];
                    if (meetEnd) {                      // 上一个字符是单引号
                        if (sqlCString[i] == '\'') {    // 又是单引号
                            meetEnd = NO;               // 继续
                        }else{                          // 不是单引号,说明字符结束了
                            tmpCString[j] = '\0';
                            break;
                        }
                    }else{
                        if (sqlCString[i] == '\'') {    // 下一个是单引号
                            meetEnd = YES;
                        }
                    }
                }
                break;
            case '`':
                // 反单引号 取所有内容
                while (i < sqlCStringLength) {
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (meetEnd) {
                        tmpCString[j] = '\0';
                        break;
                    }else{
                        if (sqlCString[i] == '`') {
                            meetEnd = YES;
                        }
                    }
                }
                break;
            default:
                // 其它字符,遇到空格或逗号结束, 遇到左小括号就直到右小括号结束
                while (i < sqlCStringLength) {
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (sqlCString[i] == '('){
                        split_string_duplicate(')', j);
                        j = strlen(tmpCString);
                    }
                    if (sqlCString[i] == ' ' || sqlCString[i] == ',') {
                        tmpCString[j] = '\0';
                        break;
                    }
                }
                break;
        }
    };
    // 复制上面这个block,使其可在其内部调用
    split_string_duplicate = split_string;
    
    Field fields[100] = {EmptyField};
    __block Field *fields_pointer = fields;    // 存放所有字段的数组
    __block char *tmp_field_name = NULL;         // 存放一个字段名
    __block BOOL meetAS = NO;           // 遇到 AS 关键字
    
    
    void (^field_parser)(char *) = ^(char *field_word) {
        if (field_word == NULL || strcmp(field_word, ",") == 0) {
            // 遇到逗号,或字段结束了,把临时字段名里的字符存入字段
            if (tmp_field_name != NULL) {
                if (meetAS) {
                    strcpy(fields_pointer->name, tmp_field_name);
                    meetAS = NO;
                }else{
                    strcpy(fields_pointer->orignal_name, tmp_field_name);
                }
                free(tmp_field_name);
                tmp_field_name = NULL;
                fields_pointer ++;
            }
        }else if(strcmp(field_word, "as") == 0){
            // 遇到 as 原始字段名结束
            if (tmp_field_name != NULL) {
                strcpy(fields_pointer->orignal_name, tmp_field_name);
                free(tmp_field_name);
                tmp_field_name = NULL;
            }
            meetAS = YES;
        }else{
            if (tmp_field_name == NULL) {
                tmp_field_name = malloc(sizeof(char) * (strlen(field_word) + 1));
                strcpy(tmp_field_name, field_word);
            }else{
                // 连续的单词
                tmp_field_name = realloc(tmp_field_name, (strlen(tmp_field_name) + 1 + strlen(field_word) + 1));
                strcat(tmp_field_name, " ");
                strcat(tmp_field_name, field_word);
            }
        }
    };
    BOOL meetField = NO;
    while (i < sqlCStringLength) {
    loop_start:
        switch (sqlCString[i]) {
            case '(':
                split_string(')', 0);
                break;
            case '\'':
                split_string('\'', 0);
                break;
            case '`':
                split_string('`', 0);
                break;
            case ' ':
                while (i++ < sqlCStringLength) {
                    if (sqlCString[i] != ' ') {
                        break;
                    }
                }
                goto loop_start;
                break;
            default:
                split_string(sqlCString[i], 0);
                break;
        }
        if (meetField == YES) {
            if (strlen(tmpCString)>0) {
                if (strcmp(tmpCString, "from") == 0) {
                    field_parser(NULL);
                    break;
                }
                field_parser(tmpCString);
            }
        }else if (strcmp(tmpCString, "select") == 0) {
            meetField = YES;
        }
        tmpCString[0] = '\0';
    }
    // 释放内存
    if (tmp_field_name != NULL) {
        free(tmp_field_name);
        tmp_field_name = NULL;
    }
    free(tmpCString);
    tmpCString = NULL;
    int fi = 0;
    while (!SameField(fields[fi], EmptyField)) {
        printf("name:%-20s, orignal_name:%-20s\n", fields[fi].name, fields[fi].orignal_name);
        fi ++;
    }
}


猜你喜欢

转载自blog.csdn.net/mlibai/article/details/47726845