如何在Objective-C中将unichar值转换为NSString?

31

我有一个国际字符存储在一个unichar变量中。这个字符不是来自文件或URL。该变量本身仅存储一个无符号短整型数(0xce91),它是以UTF-8格式编码的并且可以被转换为希腊大写字母'A'。我试图将该字符放入一个NSString变量中,但一直失败。

我尝试了两种不同的方法,都没有成功:

unichar greekAlpha = 0xce91; //could have written greekAlpha = 'Α' instead.

NSString *theString = [NSString stringWithFormat:@"Greek Alpha: %C", greekAlpha];

不好,我得到了一些奇怪的中文字符。顺便说一下,这对英文字符完美地适用。

然后我也尝试了这个:

NSString *byteString = [[NSString alloc] initWithBytes:&greekAlpha
                                                length:sizeof(unichar)
                                              encoding:NSUTF8StringEncoding];

但是这也不起作用。 显然,我做错了什么,但我不知道是什么。 有人可以帮我吗? 谢谢!


您可以通过编辑器工具栏上的小按钮(101010)来格式化代码,以获得更好的排版效果。 - hallski
1
你确定在这里使用 unichar 是正确的数据类型吗?将 UTF-8 数据存储在旨在用于 UTF-16 的类型中似乎不太对。正如 Jon Jardine 所指出的,有些 UTF-8 字符超过了 16 位... 你能否也为单个字符使用 NSString (NSString greekAlpha = @"Α"),然后在 stringWithFormat 中使用 %@ - David Gelhar
0xce91实际上是“HANGUL SYLLABLE KAEG”的Unicode编码,所以我不明白为什么你期望从中得到一个Alpha字符。你似乎被LATIN SMALL LETTER ALPHA的UTF-8编码0xc9、0x91所迷惑了。这是完全不同的编码。该字符的Unicode值为0x0251。 - gnasher729
5个回答

55
unichar greekAlpha = 0x0391;
NSString* s = [NSString stringWithCharacters:&greekAlpha length:1];

现在您可以以任何喜欢的方式将该NSString与另一个字符串合并。请注意,现在可以在NSString文字字面量中直接键入希腊字母α。


这对于一个字符完美地运行。不确定为什么会被踩。+1 - Thomas Müller
为什么这不是答案? - stonedauwg
unichar 是一个无符号短整型...仅支持16位。如果您有一个32位的Unicode字符,例如表情符号,它将无法工作。例如:0x1F347。 - Billy
@Billy,问题的提出并不是关于表情符号,而是关于希腊字母α。 - matt

21

由于0xce91是UTF-8格式,而%C期望的是UTF-16格式,所以像上面那样简单的解决方案行不通。 要使 stringWithFormat:@"%C" 生效,您需要输入0x391,这是UTF-16 Unicode。

要从UTF-8编码的unichar创建字符串,您需要首先将unicode拆分为其八位字节序列,然后使用initWithBytes:length:encoding

unichar utf8char = 0xce91; 
char chars[2];
int len = 1;

if (utf8char > 127) {
    chars[0] = (utf8char >> 8) & (1 << 8) - 1;
    chars[1] = utf8char & (1 << 8) - 1; 
    len = 2;
} else {
    chars[0] = utf8char;
}

NSString *string = [[NSString alloc] initWithBytes:chars
                                            length:len 
                                          encoding:NSUTF8StringEncoding];

我仔细查看了我发布的链接后也意识到了同样的问题。我已经更新了我的回答,并提供了解决您问题的方法。 - hallski
4
谢谢,这正是我在寻找的内容!所以,我的代码有些混乱 : )。虽然我是这个网站的新成员,但我已经使用它相当长的时间了(主要是关于C#方面,现在只是初步了解Objective-C),我发现有些人非常愿意帮助别人,这让我感到惊讶和感激。再次感谢!: ) - Terry
@Terry - StackOverflow是程序员懒惰的反例。一个奇怪的现象是,99%的Linux从未经过代码审查,但几乎每个SO的问题、答案和评论都经过了审查、编辑、投票和讨论。SO的特性也在MetaStackOverflow上经过了同样的处理。 - ArtOfWarfare

2

上述答案很好,但没有考虑长度超过16位的UTF-8字符,例如省略号符号-0xE2,0x80,0xA6。以下是对代码的微调:

if (utf8char > 65535) {
   chars[0] = (utf8char >> 16) & 255;
   chars[1] = (utf8char >> 8) & 255;
   chars[2] = utf8char & 255; 
   chars[3] = 0x00;
} else if (utf8char > 127) {
    chars[0] = (utf8char >> 8) & 255;
    chars[1] = utf8char & 255; 
    chars[2] = 0x00;
} else {
    chars[0] = utf8char;
    chars[1] = 0x00;
}
NSString *string = [[[NSString alloc] initWithUTF8String:chars] autorelease];

注意不需要长度参数的不同字符串初始化方法。

3
“unichar” 是一个16位类型,所以 utf8char 不能容纳超过16位的值。 - David Gelhar

1

这里是一个关于单个字符的UTF-8编码算法:

if (utf8char<0x80){ 
    chars[0] = (utf8char>>0)  & (0x7F | 0x00);
    chars[1] = 0x00;
    chars[2] = 0x00;
    chars[3] = 0x00;
}
else if (utf8char<0x0800){
    chars[0] = (utf8char>>6)  & (0x1F | 0xC0);
    chars[1] = (utf8char>>0)  & (0x3F | 0x80);
    chars[2] = 0x00;
    chars[3] = 0x00;
}
else if (utf8char<0x010000) {
    chars[0] = (utf8char>>12) & (0x0F | 0xE0);
    chars[1] = (utf8char>>6)  & (0x3F | 0x80);
    chars[2] = (utf8char>>0)  & (0x3F | 0x80);
    chars[3] = 0x00;
}
else if (utf8char<0x110000) {
    chars[0] = (utf8char>>18) & (0x07 | 0xF0);
    chars[1] = (utf8char>>12) & (0x3F | 0x80);
    chars[2] = (utf8char>>6)  & (0x3F | 0x80);
    chars[3] = (utf8char>>0)  & (0x3F | 0x80);
}

1
上面的代码相当于 unichar foo = 'abc';
问题在于,'Α' 在“执行字符集”(我假设是UTF-8)中不能映射为单个字节,这在C99 §6.4.4.4 10中是“实现定义”的:

包含多个字符(例如'ab')或包含不映射到单字节执行字符的字符或转义序列的整数字符常量的值是实现定义的。

一种方法是将 'ab' 等于 'a'<<8|b。一些Mac/iOS系统头文件依赖于此,用于诸如OSType/FourCharCode/FourCC的东西;iOS中我能想到的唯一一个是CoreVideo像素格式。然而,这是不可移植的。
如果您真的需要一个unichar文字,可以尝试使用L'A'(从技术上讲,它是一个wchar_t文字,但在OS X和iOS上,wchar_t通常是UTF-16,因此它可以用于BMP内的内容)。然而,更简单的方法是使用@"Α"(只要正确设置源字符编码即可工作)或@"\u0391"(自iOS 3 SDK以来就可以使用)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接