一个函数,可以提取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 ++;
}
}