最近在使用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互转