cocos2d-x 显示中文字符和解析XML文件

来源:互联网 发布:js获取第一个class 编辑:程序博客网 时间:2024/05/22 04:43

作者:Alex Zhou 时间:2013-01-26

标签:CCSAXParser,cocos2d-x开发专栏,iconv,显示中文字符
文章摘要: 在cocos2d-x中直接显示中文的时候会出现乱码,虽然在实际开发中把字符串直接写在代码里也不是好的做法,但是有时候也是为了更方便了。 本文采用两种方案来解决这个问题: 1. 使用iconv,引擎也提供了这个库,不过只是win32平台,移植到android上还得自己去下载iconv库编译。 2. 把字符串写到xml文件中,然后解析xml文件,格…

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

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

1
2
3
4
5
6
7
8
#ifndef ICONV_STRING_H
#define ICONV_STRING_H
 
intconvert(char*from_charset, char*to_charset, char*inbuf, size_tinlen, char*outbuf, size_toutlen);
 
intgbk2utf8(char*inbuf, size_tinlen, char*outbuf, size_toutlen);
 
#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
 
intconvert(char*from_charset, char*to_charset, char*inbuf, size_tinlen, char*outbuf, size_toutlen)
{
    iconv_t iconvH;
    iconvH = iconv_open(to_charset, from_charset);
    if( !iconvH ) returnNULL;
    memset(outbuf, 0, outlen);
 
 #if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    constchar *temp = inbuf;
    constchar **pin = &temp;
    char**pout = &outbuf;
    if( !iconv(iconvH, pin, &inlen, pout, &outlen) )
    {
        iconv_close(iconvH);
        returnNULL;
    }
#else
    if( !iconv(iconvH, &inbuf, &inlen, &outbuf, &outlen) )
    {
        iconv_close(iconvH);
        returnNULL;
    }
#endif
    iconv_close(iconvH);
    returnNULL;
}
 
intgbk2utf8(char*inbuf, size_tinlen, char*outbuf, size_toutlen)
{
    returnconvert("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_tinLen = strlen(inBuf);
size_toutLen = 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"
 
classXMLParser : publiccocos2d::CCObject, publiccocos2d::CCSAXDelegator
{
public:
    staticXMLParser* parseWithFile(constchar *xmlFileName);
 
    staticXMLParser* parseWithString(constchar *content);
 
    XMLParser();
    virtual~XMLParser();
 
    // 从本地xml文件读取
    boolinitWithFile(constchar *xmlFileName);
 
    // 从字符中读取,可用于读取网络中的xml数据
    boolinitWithString(constchar *content);
 
    /**
    *对应xml标签开始,如:<string name="alex">, name为string,atts为string的属性,如["name","alex"]
    */
    virtualvoid startElement(void*ctx, constchar *name, constchar **atts);
 
    /**
    *对应xml标签结束,如:</string>
    */
    virtualvoid endElement(void*ctx, constchar *name);
 
    /**
    *对应xml标签文本,如:<string name="alex">Alex Zhou</string>的Alex Zhou
    */
    virtualvoid textHandler(void*ctx, constchar *s, intlen);
 
    cocos2d::CCString* getString(constchar *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"
 
usingnamespace std;
usingnamespace cocos2d;
 
// 空格
conststatic int SPACE = 32;
// 换行
conststatic int NEXTLINE = 10;
// tab 横向制表符
conststatic int TAB = 9;
 
XMLParser* XMLParser::parseWithFile(constchar *xmlFileName)
{
    XMLParser *pXMLParser = newXMLParser();
    if( pXMLParser->initWithFile(xmlFileName) )
    {
        pXMLParser->autorelease();  
        returnpXMLParser;
    }
    CC_SAFE_DELETE(pXMLParser);
    returnNULL;
}
 
boolXMLParser::initWithFile(constchar *xmlFileName)
{
    m_pDictionary = newCCDictionary();
    CCSAXParser _parser;
    _parser.setDelegator(this);
    constchar *fullPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(xmlFileName);
    return_parser.parse(fullPath);
}
 
XMLParser* XMLParser::parseWithString(constchar *content)
{
    XMLParser *pXMLParser = newXMLParser();
    if( pXMLParser->initWithString(content) )
    {
        pXMLParser->autorelease();  
        returnpXMLParser;
    }
    CC_SAFE_DELETE(pXMLParser);
    returnNULL;
}
 
boolXMLParser::initWithString(constchar *content)
{
    m_pDictionary = newCCDictionary();
    CCSAXParser _parse;
    _parse.setDelegator(this);
    return_parse.parse(content, strlen(content) );
}
 
voidXMLParser::startElement(void*ctx, constchar *name, constchar **atts)
{
    this->startXMLElement = (char*)name;
    CCLog("start=%s", startXMLElement.c_str());
    if(this->startXMLElement == "string")
    {
        while(atts && *atts)
        {
            constchar *attsKey = *atts;   
            if(0 == strcmp(attsKey,"name"))
            {
                ++ atts;
                constchar *attsValue = *atts;
                m_key = attsValue;
                break;
            }
            ++ atts;
        }
 
    }
 
}
 
voidXMLParser::endElement(void*ctx, constchar *name)
{
    this->endXMLElement = (char*)name;
    CCLog("end=%s", endXMLElement.c_str());
}
 
voidXMLParser::textHandler(void*ctx, constchar *s, intlen)
{
    string value((char*)s, 0, len);
    //是否全是非正常字符
    boolnoValue = true;
    for(inti = 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(constchar *key)
{
    string strKey(key);
    return(CCString *)this->m_pDictionary->objectForKey(strKey);
}
 
XMLParser::XMLParser()
{
}
 
XMLParser::~XMLParser()
{
    CC_SAFE_DELETE(this->m_pDictionary);
}

如xml部分内容为:

1
<stringname="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
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
    <stringname="hello">你好</string>
    <stringname="name">Alex Zhou</string>
</resources>

效果如图:

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