zoukankan      html  css  js  c++  java
  • cocos2d-x 显示中文字符和解析XML文件 转载

    源地址:http://codingnow.cn/cocos2d-x/1038.html

    在cocos2d-x中直接显示中文的时候会出现乱码,虽然在实际开发中把字符串直接写在代码里也不是好的做法,但是有时候也是为了更方便了。
    本文采用两种方案来解决这个问题:
    1. 使用iconv,引擎也提供了这个库,不过只是win32平台,移植到android上还得自己去下载iconv库编译。
    2. 把字符串写到xml文件中,然后解析xml文件,格式按照android中的strings.xml
    这是一种更好的做法,特别是需要提供国际化支持时。
    下面来看具体的实现:
    1. 使用iconv库
    iconv的作用是将文本在多种国际编码格式之间进行转换。
    (1) 首先包含iconv.h头文件,c++->常规->附加包含目录:cocos2dxplatform hird_partywin32iconv,如图:

    (2) 创建头文件IconvString.h,源码:

    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef ICONV_STRING_H
    #define ICONV_STRING_H
     
    int convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen, char *outbuf, size_t outlen);
     
    int gbk2utf8(char *inbuf, size_t inlen, char *outbuf, size_t outlen);
     
    #endif

    (3) 创建源文件IconvString.cpp,源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include <string>
    #include "iconv.h"
     
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    // 编译链接的时候指定静态库
    #pragma comment(lib,"libiconv.lib")
    #endif
     
    int convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen, char *outbuf, size_t outlen)
    {
        iconv_t iconvH;
        iconvH = iconv_open(to_charset, from_charset);
        if( !iconvH ) return NULL;
        memset(outbuf, 0, outlen);
     
     #if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
        const char *temp = inbuf;
        const char **pin = &temp;
        char **pout = &outbuf;
        if( !iconv(iconvH, pin, &inlen, pout, &outlen) )
        {
            iconv_close(iconvH);
            return NULL;
        }
    #else
        if( !iconv(iconvH, &inbuf, &inlen, &outbuf, &outlen) )
        {
            iconv_close(iconvH);
            return NULL;
        }
    #endif
        iconv_close(iconvH);
        return NULL;
    }
     
    int gbk2utf8(char *inbuf, size_t inlen, char *outbuf, size_t outlen)
    {
        return convert("gb2312", "utf-8", inbuf, inlen, outbuf, outlen);
    }

    代码比较简单,需要注意的是win32和android对应的iconv函数参数不一样。在win32平台需要指定lib库,它是iconv.h头文件对应的源码实现。android平台需要下载iconv库,编译的时候在iconv库的根目录下创建一个Android.mk文件就行,Android.mk内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    LOCAL_PATH:= $(call my-dir)
    #libiconv.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := libiconv
    LOCAL_CFLAGS :=
      -Wno-multichar
      -DAndroid
      -DLIBDIR="c"
      -DBUILDING_LIBICONV
      -DIN_LIBRARY
     
    LOCAL_SRC_FILES :=
      libcharset/lib/localcharset.c
      lib/iconv.c
      lib/relocatable.c
     
    LOCAL_C_INCLUDES +=
      $(LOCAL_PATH)/include
      $(LOCAL_PATH)/libcharset
      $(LOCAL_PATH)/lib
      $(LOCAL_PATH)/libcharset/include
      $(LOCAL_PATH)/srclib
    include $(BUILD_STATIC_LIBRARY)

    (4) 下面来看看如何使用,源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    char *inBuf = "iconv: 你好,Alex Zhou";
    size_t inLen = strlen(inBuf);
    size_t outLen = inLen << 1;
    char *outBuf = (char *)malloc(outLen);
    gbk2utf8(inBuf, inLen, outBuf, outLen);
     
    CCLabelTTF* pLabel = CCLabelTTF::create(outBuf, "Arial", 30);
    pLabel->setColor(ccBLACK);
    free(outBuf);

    要注意转码前后的字符串占的空间是不一样的, 需要知道每种编码格式字符占的字节数:我的vs选择的是Unicode字符集,所以中文字符占2个字节,英文字符占1个字节;gb2312每个字符占2个字节,而UTF-8中文字符占3个字节,英文字符占1个字节。所以这里把存储输出的字符串的数组容量扩大了一部。
    效果图:

    2. 使用xml的方式
    这里使用了引擎提供的CCSAXParser来解析xml,它内部是用libxml2来实现的。
    (1) 创建XmlParser.h文件,源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    #ifndef XML_PARSE_H
    #define XML_PARSE_H
     
    #include <string>
    #include "cocos2d.h"
     
    class XMLParser : public cocos2d::CCObject, public cocos2d::CCSAXDelegator
    {
    public:
        static XMLParser* parseWithFile(const char *xmlFileName);
     
        static XMLParser* parseWithString(const char *content);
     
        XMLParser();
        virtual ~XMLParser();
     
        // 从本地xml文件读取
        bool initWithFile(const char *xmlFileName);
     
        // 从字符中读取,可用于读取网络中的xml数据
        bool initWithString(const char *content);
     
        /**
        *对应xml标签开始,如:<string name="alex">, name为string,atts为string的属性,如["name","alex"]
        */
        virtual void startElement(void *ctx, const char *name, const char **atts);
     
        /**
        *对应xml标签结束,如:</string>
        */
        virtual void endElement(void *ctx, const char *name);
     
        /**
        *对应xml标签文本,如:<string name="alex">Alex Zhou</string>的Alex Zhou
        */
        virtual void textHandler(void *ctx, const char *s, int len);
     
        cocos2d::CCString* getString(const char *key);
     
    private:
        cocos2d::CCDictionary *m_pDictionary;
        std::string m_key;
     
        std::string startXMLElement;
        std::string endXMLElement;
     
    };
     
    #endif

    代码里已对主要的函数进行了解释,这里就不啰嗦了。
    (2) 创建XmlParser.cpp,源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    #include "XMLParser.h"
     
    using namespace std;
    using namespace cocos2d;
     
    // 空格
    const static int SPACE = 32;
    // 换行
    const static int NEXTLINE = 10;
    // tab 横向制表符
    const static int TAB = 9;
     
    XMLParser* XMLParser::parseWithFile(const char *xmlFileName)
    {
        XMLParser *pXMLParser = new XMLParser();
        if( pXMLParser->initWithFile(xmlFileName) )
        {
            pXMLParser->autorelease();  
            return pXMLParser;
        }
        CC_SAFE_DELETE(pXMLParser);
        return NULL;
    }
     
    bool XMLParser::initWithFile(const char *xmlFileName)
    {
        m_pDictionary = new CCDictionary();
        CCSAXParser _parser;
        _parser.setDelegator(this);
        const char *fullPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(xmlFileName);
        return _parser.parse(fullPath);
    }
     
    XMLParser* XMLParser::parseWithString(const char *content)
    {
        XMLParser *pXMLParser = new XMLParser();
        if( pXMLParser->initWithString(content) )
        {
            pXMLParser->autorelease();  
            return pXMLParser;
        }
        CC_SAFE_DELETE(pXMLParser);
        return NULL;
    }
     
    bool XMLParser::initWithString(const char *content)
    {
        m_pDictionary = new CCDictionary();
        CCSAXParser _parse;
        _parse.setDelegator(this);
        return _parse.parse(content, strlen(content) );
    }
     
    void XMLParser::startElement(void *ctx, const char *name, const char **atts)
    {
        this->startXMLElement = (char *)name;
        CCLog("start=%s", startXMLElement.c_str());
        if(this->startXMLElement == "string")
        {
            while(atts && *atts)
            {
                const char *attsKey = *atts;   
                if(0 == strcmp(attsKey, "name"))
                {
                    ++ atts;
                    const char *attsValue = *atts;
                    m_key = attsValue;
                    break;
                }
                ++ atts;
            }
     
        }
     
    }
     
    void XMLParser::endElement(void *ctx, const char *name)
    {
        this->endXMLElement = (char *)name;
        CCLog("end=%s", endXMLElement.c_str());
    }
     
    void XMLParser::textHandler(void *ctx, const char *s, int len)
    {
        string value((char *)s, 0, len);
        //是否全是非正常字符
        bool noValue = true;
        for(int i = 0; i < len; ++i)
        {
            if(s[i] != SPACE && s[i] != NEXTLINE && s[i] != TAB)
            {
                noValue = false;   
                break;
            }
        }
        if(noValue) return;
        CCString *pString = CCString::create(value);
        CCLog("key=%s value=%s", m_key.c_str(), pString->getCString());
        this->m_pDictionary->setObject(pString, this->m_key);
    }
     
    CCString* XMLParser::getString(const char *key)
    {
        string strKey(key);
        return (CCString *)this->m_pDictionary->objectForKey(strKey);
    }
     
    XMLParser::XMLParser()
    {
    }
     
    XMLParser::~XMLParser()
    {
        CC_SAFE_DELETE(this->m_pDictionary);
    }

    如xml部分内容为:

    1
    <string name="a">b</string>

    上面的代码的作用是把key=a,value=b存储到字典中。
    (3)下面来看看如何使用XmlParser,源码:

    1
    2
    3
    4
    5
    6
    XMLParser *pXmlParser = XMLParser::parseWithFile("strings.xml");
    CCString *pValue1 = pXmlParser->getString("hello");
    CCString *pValue2 = pXmlParser->getString("name");
    CCString *pValue = CCString::createWithFormat("%s%s%s%s", "XML: ", pValue1->getCString(), ",", pValue2->getCString());
    CCLabelTTF* pLabel = CCLabelTTF::create(pValue->getCString(), "Arial", 30);
    pLabel->setColor(ccBLACK);

    strings.xml的内容:

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="hello">你好</string>
        <string name="name">Alex Zhou</string>
    </resources>

    效果如图:

    ok,到这里就结束了,android对应的iconv库我已经打包到源码中了。
    源码:http://download.csdn.net/detail/zhoujianghai/5031137

    2013年8月11号更新:
    有朋友反馈说在xml中的换行符(“ ”)不起作用,原因是XMLParser解析xml时,把” ”变成了”\n”,这样就会把换行符原样输出了,解决方法很简单,直接把”\n”替换成” ”就可以了,添加一个字符串替换函数。原理:遍历原字符串,查找要替换的字符串在原字符串中的位置pos,然后截取从i到pos的子字符串再跟新字符串拼接,然后更新i,继续查找。在XmlParser.cpp中添加下面的字符串替换函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    string replace(string source, string pattern, string dstPattern)
    {
        string result;
        string::size_type pos;
     
        int lenSource = source.length();
        int i = 0;
     
        for(i = 0; i < lenSource; ++i)
        {
            pos = source.find(pattern, i);
            if(string::npos == pos)
                break;
     
            if(pos < lenSource)
            {
                string s = source.substr(i, pos - i);
                result += s;
                result += dstPattern;
                i = pos + pattern.length() - 1;
            }
        }
        result += source.substr(i);
        return result;
    }

    修改XmlParser.cpp的textHandler函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    void XMLParser::textHandler(void *ctx, const char *s, int len)
    {
        if(this->m_key.length() == 0)
            return;
     
        string value((char*)s, 0, len);
        CCLog("s=%s, len=%d", value.c_str(), value.length());
        //是否全是非正常字符
        bool noValue = true;
     
        for(int i = 0; i < value.length(); ++i)
        {
            char c = value.at(i);
            CCLog("v=%d", c);
     
            if(c != SPACE && c != NEXTLINE && c != TAB)
            {
                noValue = false;   
                break;
            }
        }
        if(noValue) return;
        string result = replace(value, string("\n"), string(" "));
        CCString *pString = CCString::create(result);
        CCLog("key=%s value=%s", m_key.c_str(), pString->getCString());
        this->m_pDictionary->setObject(pString, this->m_key);
    }

    更新strings.xml:

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="hello">你好世界 </string>
        <string name="name">Alex Zhou</string>
    </resources>

    运行效果如下图:

  • 相关阅读:
    Nginx配置图片请求
    Nginx 配置浏览Linux 系统目录并下载文件
    SpringBoot + Dubbo + Zookper 整合
    mysql 随机选取一条符合条件的记录
    linux 安装rabbitMQ详细教程
    spring boot 实现redis 的key的过期监听,执行自己的业务
    springboot 配置将info、error、debug 分别输出到不同文件
    使用 mvn install 命令将本地jar包注册到本地maven仓库
    关于Snowflake 生成53位ID
    spring boot 或 spring 集成 atomikos jta 完成多数据源事务管理
  • 原文地址:https://www.cnblogs.com/Z-XML/p/3413261.html
Copyright © 2011-2022 走看看