在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 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中的换行符(“\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; 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( "\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
|
<? xml version = "1.0" encoding = "utf-8" ?> < resources > < string name = "hello" >你好世界\n</ string > < string name = "name" >Alex
Zhou</ string > </ resources > |
运行效果如下图:
cocos2d-x中中文乱码解决,布布扣,bubuko.com
原文:http://blog.csdn.net/zhanghefu/article/details/21288451