使用GDAL讀取S-57海圖資料時,對於屬性工作表中的中文屬性值讀出來是亂碼。1所示。
圖1 S57海圖資料中文亂碼欄位
通過調試代碼發現,S-57檔案中的中文是按照寬位元組儲存在檔案中,而GDAL在讀取時統一按照單位元組來讀取,這樣就直接導致了中文屬性值的亂碼。比如這裡有個欄位屬性值為“北京市”,讀出來顯示為“S琋^”,對應的十六進位為“0x17 0x53 0xac 0x4e 0x02 0x5e 0x1f 0x00”。
本以為GDAL中會有個設定選項來設定編碼,S57的源碼翻遍了也沒找到設定選項,看來只能自力更生了,那就是自己寫個轉換函式來進行轉換。修改GDAL庫的源碼工作量有點大,還是直接在外面處理吧。首先我們要寫一個把寬位元組轉為單位元組的函數。代碼如下:
string ConvertWchar2Char(const wstring &str){size_t len = wcstombs(NULL, str.c_str(), 0)*2 + 1;char *pszDst = new char[len];setlocale(LC_ALL,""); //設定本地預設Localeint len1 = wcstombs(pszDst, str.c_str(), len);setlocale(LC_ALL,"C"); //預設if(len1 == -1){delete []pszDst;throw runtime_error("wcstombs(): unable to convert character");}string strChar = string(pszDst, len);delete []pszDst;return strChar;}
有了上面的函數,我們就可以在讀取屬性值後,調用上面的函數進行轉換就OK了。需要注意的是,GDAL中擷取的屬性值傳回值是一個const char*格式,表面看起來是個單位元組,但實質記憶體儲存的確是多位元組,所以我們需要強制類型轉換轉為多位元組,程式碼片段如下:
const char* pszValue = poFeature->GetFieldAsString("NOBJNM");wstring strwValue = (const wchar_t*)pszValue;//轉換為單位元組string strValue = ConvertWchar2Char(strwValue);
第一句返回的是一個const char*,然後直接強制類型轉為const wchar_t*類型,然後構造一個wstring類型。最後使用上面的函數進行轉換即可得到最終的結果值。完整的測試代碼如下:
#include <stdio.h>#include <string>#include "ogrsf_frmts.h"#include "ogr_spatialref.h"using namespace std;string ConvertWchar2Char(const wstring &str){size_t len = wcstombs(NULL, str.c_str(), 0)*2 + 1;char *pszDst = new char[len];setlocale(LC_ALL,""); //設定本地預設Localeint len1 = wcstombs(pszDst, str.c_str(), len);setlocale(LC_ALL,"C"); //預設if(len1 == -1){delete []pszDst;throw runtime_error("wcstombs(): unable to convert character");}string strChar = string(pszDst, len);delete []pszDst;return strChar;}int ReadS57() { CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO"); OGRRegisterAll(); //開啟資料 const char* pszS57 = "D:\\C1100102.000"; OGRDataSource *poDS = OGRSFDriverRegistrar::Open(pszS57, FALSE ); if( poDS == NULL ) { printf( "Open failed.\n" ); return 1; } // 擷取有中文屬性值的圖層 OGRLayer *poLayer = poDS->GetLayerByName( "BUAARE" ); if( poLayer == NULL ) { printf( "Get Layer failed.\n" ); OGRDataSource::DestroyDataSource( poDS ); return 1; } poLayer->ResetReading(); OGRFeature *poFeature = poLayer->GetNextFeature(); while (poFeature != NULL ) { OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); //擷取屬性欄位值 // 為了示範說明,就只輸出亂碼的屬性值 string strValue = poFeature->GetFieldAsString("NOBJNM"); if (strValue != ""){char* pszValue = (char*)strValue.c_str();size_t ilast = strlen(pszValue);pszValue[ilast-1] = '\0';wstring strwValue = (const wchar_t*)pszValue; //轉換為單位元組 strValue = ConvertWchar2Char(strwValue); }printf("%s\n", strValue.c_str()); OGRFeature::DestroyFeature( poFeature ); poFeature = poLayer->GetNextFeature(); } OGRDataSource::DestroyDataSource( poDS ); return 0; }int main(){// 先測試轉換函式是否正常工作const char* pszValue = "S琋^";wstring str = (const wchar_t*)pszValue;string strTemp = ConvertWchar2Char(str);printf("%s\n", strTemp.c_str());wstring str1 = L"Hello1234";strTemp = ConvertWchar2Char(str1);printf("%s\n", strTemp.c_str());// 讀取S57海圖資料ReadS57();return 0;}