使用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
#include
#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;
}