在将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
看不懂啊,如果谁看懂这是什么语法,请告诉我,在此谢过。