原文地址: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++->常规->附加包含目录:cocos2dx\platform\third_party\win32\iconv,如图:
(2) 创建头文件IconvString.h,源码:
|
1
2
3
4
5
6
7
8
|
#ifndef
ICONV_STRING_H#define
ICONV_STRING_Hintconvert(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") #endifintconvert(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.soinclude
$(CLEAR_VARS)LOCAL_MODULE
:= libiconvLOCAL_CFLAGS
:= \ -Wno-multichar
\ -DAndroid
\ -DLIBDIR="c"\ -DBUILDING_LIBICONV
\ -DIN_LIBRARYLOCAL_SRC_FILES
:= \ libcharset/lib/localcharset.c
\ lib/iconv.c
\ lib/relocatable.cLOCAL_C_INCLUDES
+= \ $(LOCAL_PATH)/include\ $(LOCAL_PATH)/libcharset\ $(LOCAL_PATH)/lib\ $(LOCAL_PATH)/libcharset/include\ $(LOCAL_PATH)/srclibinclude
$(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.youkuaiyun.com/detail/zhoujianghai/5031137
2013年8月11号更新:
有朋友反馈说在xml中的换行符(“\n”)不起作用,原因是XMLParser解析xml时,把”\n”变成了”\\n”,这样就会把换行符原样输出了,解决方法很简单,直接把”\\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; intlenSource
= source.length(); inti
= 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); returnresult;} |
修改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
|
voidXMLParser::textHandler(void*ctx,
constchar
*s, intlen){ if(this->m_key.length()
== 0) return; string
value((char*)s,
0, len); CCLog("s=%s,
len=%d",
value.c_str(), value.length()); //是否全是非正常字符 boolnoValue
= true; for(inti
= 0; i < value.length(); ++i) { charc
= 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("\n")); 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
|
<?xmlversion="1.0"encoding="utf-8"?><resources> <stringname="hello">你好世界\n</string> <stringname="name">Alex
Zhou</string></resources> |
运行效果如下图:

829

被折叠的 条评论
为什么被折叠?



