最近在使用GDAL讀寫Shp格式中的屬性字段的時候也遇到了中文亂碼的問題,總結下本身遇到的狀況。編輯器
應該是因爲shp格式加入了對寬字符的支持,因此致使有段時間的shp文件和ArcGIS是存在不匹配的問題,因此在網上搜索資源的時候遇到了大量的關於ArcMap顯示shp屬性表出現亂碼的問題。如今的shp格式的文件應該已經穩定下來了,新添加了一個.cpg的文件,裏面保存着屬性表的編碼格式:ide
從ArcGIS10.2開始,只要是屬性表編碼與.cpg文件記錄的編碼方式一致,就不會再有顯示亂碼的問題。網上查詢到的修改註冊表的方法,我在ArcGIS10.2中試過,彷佛已經再也不起效了。函數
那麼對於沒有.cpg或者的狀況,應該能夠看屬性表.dbf文件。若是編碼方式正確,這個文件用文本編輯器打開是能夠看到正常的中文的:編碼
在正常顯示中文狀況下,能夠查看下文件的編碼方式:.net
固然,若是遇到亂碼,能夠嘗試用別的編碼方式打開,這樣你就能知道屬性表具體是什麼編碼了。對於國內的狀況來講,只有ANSI編碼和UNICODE編碼兩種:其中簡體中文系統中ANSI編碼就是GB2312編碼;UTF-8是UNICODE編碼的一種具體實現。3d
能夠經過全局設置函數CPLSetConfigOption(),來配置讀取Shp文件的讀取編碼。例如對於簡體中文系統中ANSI編碼,能夠設置爲GBK:code
CPLSetConfigOption("SHAPE_ENCODING","GBK");
上面這種方式是全局設置的,若是想設置單個文件的編碼方式也是能夠的。例如,打開一個矢量文件讀取爲UTF-8的數據集:htm
char** ppszOptions = NULL; ppszOptions = CSLSetNameValue(ppszOptions, "ENCODING", "UTF-8"); GDALDataset *poDS = (GDALDataset*)GDALOpenEx(filePath.c_str(), GDAL_OF_VECTOR, NULL, ppszOptions, NULL);
網上提供的解決方案都是將編碼方式設置爲空[1],這種方式應該更具備通用性,起碼我這裏讀取GBK和UTF-8格式的Shp的格式都是能夠的:blog
CPLSetConfigOption("SHAPE_ENCODING","");
若是讀取出來的字段屬性仍然是亂碼,就應該考慮字符串的解碼問題,就是獲取的字段屬性字符串沒有正確的解碼出來。例如讀取UTF-8的Shp文件的屬性字段:
OGRFeature *poFeature; while ((poFeature = poLayer->GetNextFeature()) != NULL) { OGRGeometry *pGeo = poFeature->GetGeometryRef(); OGRwkbGeometryType pGeoType = pGeo->getGeometryType(); // OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); int n = poFDefn->GetFieldCount(); //得到字段的數目,不包括前兩個字段(FID,Shape); for (int iField = 0; iField <n; iField++) { //輸出每一個字段的值 //cout << poFeature->GetFieldAsString(iField) << " "; cout << UTF8_To_string(poFeature->GetFieldAsString(iField)) << " "; } //cout << endl; OGRFeature::DestroyFeature(poFeature); }
默認狀況下,cout是沒法正確打印輸出UTF-8字符編碼的,經過UTF8_To_string這個函數,將UTF-8編碼的字符串轉換成本地ANSI編碼,也就是GBK編碼字符串,就能夠正確輸出顯示了。附帶一下二者的轉換函數[2]:
// UTF8轉std:string // 轉換過程:先將utf8轉雙字節Unicode編碼,再經過WideCharToMultiByte將寬字符轉換爲多字節。 std::string UTF8_To_string(const std::string& str) { int nwLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0); wchar_t* pwBuf = new wchar_t[nwLen + 1]; //必定要加1,否則會出現尾巴 memset(pwBuf, 0, nwLen * 2 + 2); MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), pwBuf, nwLen); int nLen = WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL); char* pBuf = new char[nLen + 1]; memset(pBuf, 0, nLen + 1); WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL); std::string strRet = pBuf; delete []pBuf; delete []pwBuf; pBuf = NULL; pwBuf = NULL; return strRet; } // std:string轉UTF8 std::string string_To_UTF8(const std::string& str) { int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0); wchar_t* pwBuf = new wchar_t[nwLen + 1]; //必定要加1,否則會出現尾巴 ZeroMemory(pwBuf, nwLen * 2 + 2); ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen); int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL); char* pBuf = new char[nLen + 1]; ZeroMemory(pBuf, nLen + 1); ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL); std::string strRet(pBuf); delete []pwBuf; delete []pBuf; pwBuf = NULL; pBuf = NULL; return strRet; }
還有個值得注意的問題就是Shp格式的屬性字段名稱的長度最大隻能支持10個字符。若是採用UTF-8編碼,可能用不了幾個中文字符就被截斷了,這個時候屬性字段名稱也可能存在亂碼。
[1] GDAL/OGR 1.9.0獲取shp文件中中文字段值和屬性值亂碼文件解決
[2] UTF8與std:string互轉