在将GDAL更新至1.8.1之后,发现之前写的代码有些不能用了,前几天发现不能打开带有汉字的路径文件,并将其修改,详细参见我的CSDN博客,今天又发现在使用OGR_L_SetAttributeFilter的时候,之前可以进行过滤,现在err一直返回5,并且提示“语法错误”,代码如下:
const char* pszSQL = "Name=昌平区"; OGRErr err = OGR_L_SetAttributeFilter(hLayer, pszSQL);
对GDAL代码进行调试后发现,在1.8之前的版本,源代码中,使用的是一个swq.c的文件,但是现在用的是swq.cpp的文件,发现还多了好多的文件,具体就是以swq_开头的几个文件。
函数OGR_L_SetAttributeFilter之中调用的最终函数就是swq.cpp中的588行,如下:
/************************************************************************/ /* swq_expr_compile2() */ /************************************************************************/ CPLErr swq_expr_compile2( const char *where_clause, swq_field_list *field_list, swq_expr_node **expr_out ) { swq_parse_context context; context.pszInput = where_clause; context.pszNext = where_clause; context.nStartToken = SWQT_LOGICAL_START; if( swqparse( &context ) == 0 && context.poRoot->Check( field_list ) != SWQ_ERROR ) { *expr_out = context.poRoot; return CE_None; } else { delete context.poRoot; *expr_out = NULL; return CE_Failure; } } 上面得代码中,核心的函数就是那个swqparse( &context ) == 0,仔细研究发现该函数在文件GDAL_HOME\ogr\swq_parser.cpp的67行定义,只不过是个宏定义,最后发现是在第1365行定义的,具体代码见下: /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (swq_parse_context *context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (swq_parse_context *context) #else int yyparse (context) swq_parse_context *context; #endif #endif { //此处为函数体,太多,不方便贴过来 }
在上面的函数中最关键的一个函数叫yylex,一看又是一个宏定义,好吧,原来的函数叫swqlex,这个函数的位置在swq.cpp中的第一个函数就是,这个函数的重要作用就是将上面输入的过滤字符串进行分类,如果字符串第一个是““”或者”‘“,那么就把该字符串当做SWQT_STRING类型处理,如果是0~9之间的,当做SWQT_NUMBER类型处理,如果是其他的字母或者数字,这里用的函数是isalnum,该函数说明,参考这里,在这里会判断是否是SQL语句中的一些关键字,入,IN,LIKE,ILIKE,ESCAPE,NULL,IS,NOT,AND,OR,BETWEEN等。到现在我们再回到开始的问题,为什么将字符串”NAME=昌平区“传入进来后,会提示语法错误呢,通过上面的分析,这个字符串,第一个字符既不是引号也不是数字,那么久进入到第三种情况了,然后解释器开始查找NAME,找了半天,没有发现NAME这么一个关键字,起结果就是提示”语法错误“。
知道了上面的工作过程,那么就知道怎么修改了,就在NAME和昌平区两个字符串前后都加上引号,如下
“NAME”= “昌平区”
再次进行测试,程序正常通过。
PS:如果把文件swq_parser.cpp能看懂的话,那么你的C/C++水平已经非常的牛X了,我是没看懂,这个文件其实是bison中的一部分,仔细搜索,发现bison是gnu下面的一个专门负责语法解释的开源库,中文的介绍可以参考这里。在这个文件中,大量使用了goto语句,宏定义,以及我第一次见到的”#line 127 "swq_parser.cpp"”之类写法,此外还有个跟变态的y文件,路径为:GDAL_HOME\ogr\swq_parser.y。这个文件是嵌入在swq_parser.cpp中的,比如第1271行中的代码,如下:
switch (yytype) { case 3: /* "SWQT_NUMBER" */ /* Line 1000 of yacc.c */ #line 89 "swq_parser.y" { delete (*yyvaluep); }; /* Line 1000 of yacc.c */ #line 1277 "swq_parser.cpp" break; case 4: /* "SWQT_STRING" */ /* Line 1000 of yacc.c */ #line 89 "swq_parser.y" { delete (*yyvaluep); }; /* Line 1000 of yacc.c */ #line 1286 "swq_parser.cpp" break; case 5: /* "SWQT_IDENTIFIER" */
这串代码,我没看懂,大概意思可能就是将y文件对应的行数所在位置进行执行,比如y文件的89行的内容是下面这样写的:
%destructor { delete $$; } SWQT_NUMBER SWQT_STRING SWQT_IDENTIFIER
看不懂啊,如果谁看懂这是什么语法,请告诉我,在此谢过。