zoukankan      html  css  js  c++  java
  • 关于GDAL读写Shp乱码的问题总结

    1. 正文

    最近在使用GDAL读写Shp格式中的属性字段的时候也遇到了中文乱码的问题,总结下自己遇到的情况。

    1.1. shp文件本身的编码的问题

    应该是由于shp格式加入了对宽字符的支持,所以导致有段时间的shp文件和ArcGIS是存在不匹配的问题,所以在网上搜索资源的时候遇到了大量的关于ArcMap显示shp属性表出现乱码的问题。现在的shp格式的文件应该已经稳定下来了,新添加了一个.cpg的文件,里面保存着属性表的编码格式:

    image
    图1-1:shp格式的.cpg文件

    从ArcGIS10.2开始,只要是属性表编码与.cpg文件记录的编码方式一致,就不会再有显示乱码的问题。网上查询到的修改注册表的方法,我在ArcGIS10.2中试过,似乎已经不再起效了。

    那么对于没有.cpg或者的情况,应该可以看属性表.dbf文件。如果编码方式正确,这个文件用文本编辑器打开是可以看到正常的中文的:

    image
    图1-2:shp格式的.dbf文件

    在正常显示中文情况下,可以查看下文件的编码方式:

    image
    图1-3:查看编码方式

    当然,如果遇到乱码,可以尝试用别的编码方式打开,这样你就能知道属性表具体是什么编码了。对于国内的情况来说,只有ANSI编码和UNICODE编码两种:其中简体中文系统中ANSI编码就是GB2312编码;UTF-8是UNICODE编码的一种具体实现。

    1.2. 设置读取的编码方式

    1.2.1. GDAL设置

    可以通过全局设置函数CPLSetConfigOption(),来配置读取Shp文件的读取编码。例如对于简体中文系统中ANSI编码,可以设置为GBK:

    CPLSetConfigOption("SHAPE_ENCODING","GBK");
    

    上面这种方式是全局设置的,如果想设置单个文件的编码方式也是可以的。例如,打开一个矢量文件读取为UTF-8的数据集:

    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的格式都是可以的:

    CPLSetConfigOption("SHAPE_ENCODING","");
    

    1.2.2. 解码方式

    如果读取出来的字段属性仍然是乱码,就应该考虑字符串的解码问题,就是获取的字段属性字符串没有正确的解码出来。例如读取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; 
    } 
    

    1.2.3. 其他

    还有个值得注意的问题就是Shp格式的属性字段名称的长度最大只能支持10个字符。如果采用UTF-8编码,可能用不了几个中文字符就被截断了,这个时候属性字段名称也可能存在乱码。

    2. 参考

    [1] GDAL/OGR 1.9.0获取shp文件中中文字段值和属性值乱码文件解决
    [2] UTF8与std:string互转

  • 相关阅读:
    ASP.NET Eval 求值运算的一些用法
    SQLSERVER中统计所有表的记录数
    一份很全的路由器默认初始密码集合
    将DataTable导出为excel
    如何强制修改mysql的root密码(mysql忘记密码)
    資料庫的安全(備份/回存)(console)
    ffserver和ffmpeg配合完成的实时流媒体服务
    mssql里sp_MSforeachtable和sp_MSforeachdb的用法
    ASP.NET中GridView中嵌套GridView
    How to update multiple columns of one table using values from another table?
  • 原文地址:https://www.cnblogs.com/charlee44/p/11881894.html
Copyright © 2011-2022 走看看