使用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,""); //设置本地默认Locale int 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,""); //设置本地默认Locale int 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; }