在將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 __cplusplusint yyparse (void *YYPARSE_PARAM);#elseint yyparse ();#endif#else /* ! YYPARSE_PARAM */#if defined __STDC__ || defined __cplusplusint yyparse (swq_parse_context *context);#elseint yyparse ();#endif#endif /* ! YYPARSE_PARAM *//*-------------------------.| yyparse or yypush_parse. |`-------------------------*/#ifdef YYPARSE_PARAM#if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER)intyyparse (void *YYPARSE_PARAM)#elseintyyparse (YYPARSE_PARAM) void *YYPARSE_PARAM;#endif#else /* ! YYPARSE_PARAM */#if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER)intyyparse (swq_parse_context *context)#elseintyyparse (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
看不懂啊,如果誰看懂這是什麼文法,請告訴我,在此謝過。