这是因为转义序列\Uxxxxxxxx
由编译器评估,编译器将其替换为相应的 Unicode 代码点。然后 when 方法stringWithFormat:
将格式说明符替换%i
为i
. 最后的字符串是对应\Uxxxxxxxx
的字符和表示的字符的串联i
。stringWithFormat:
用其他字符替换字符;它不会改变现有的字符。
但问题是,这里编译器看到一个不完整的转义序列,因为你只写了 7 个十六进制数字。所以它无法生成字符串并引发错误。
解决方案是在运行时生成字符(一个简单的整数值)并使用它创建一个字符串+[NSString stringWithCharacters:length]
。
但是,如果您查看标头,您会看到NSString
将其字符存储unichar
为定义为unsigned short
,即 16 位长的值,而 Unicode 代码点U+1F430
() 至少需要 17 位。
因此,您不能使用单个unichar
字符来表示该代码点。不过不用担心:您可以使用两个字符来表示它。
你迷路了?这里解释!Unicode 没有定义字符,它定义了代码点,它们是范围内的任意整数值U+0000
– U+10FFFF
。然后,实现决定如何使用字符来表示这些代码点。只要它设法表示所有有效的代码点,实现就可以使用它想要的任何数据类型作为字符。最简单的解决方案是使用 32 位长的整数,但这需要太多内存,因为您使用的大多数代码点都在第一个 Unicode 计划 ( U+0000
- U+FFFF
) 中。因此,使用 16 位长字符的UTF-16 编码NSString
存储代码点。
在 UTF-16 中,超出U+FFFF
的每个代码点都使用范围内的一对字符(称为代理对)存储0xD800
- 0xDFFF
(相应的代码点在 Unicode 标准中明确保留)。
总之,任何有效的 Unicode 代码点都可以用一个或两个unichar
字符来表示。那里描述了这样做的方法。这是一个简单的实现:
static NSString *stringWithCodePoint(uint32_t codePoint)
{
// NOTE: As I edited the answer, you'll find a simpler implementation of
// this function below
unichar characters[2];
NSUInteger length;
if ( codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF) ) {
characters[0] = codePoint;
length = 1;
}
if ( codePoint >= 0x10000 && codePoint <= 0x10ffff ) {
codePoint -= 0x10000;
characters[0] = 0xD800 + (codePoint >> 10);
characters[1] = 0xDC00 + (codePoint & 0x3ff);
length = 2;
}
else {
length = 0; // invalid code point
}
return [NSString stringWithCharacters:characters length:length];
}
现在我们可以从任何有效的代码点生成一个字符串,我们只需要更新代码以使用我们之前编写的函数:
for (int i = 0; i < 10; i++)
[someArray addObject:stringWithCodePoint(0x0001F430 + i)];
编辑:我只是想出了一种更简单的方法来NSString
从代码点获取 a 。它通过使用-[NSString initWithBytes:length:encoding:]
和NSUTF32StringEncoding
编码工作:
static NSString *stringWithCodePoint(uint32_t codePoint)
{
NSString *string = [[NSString alloc] initWithBytes:&codePoint length:4 encoding:NSUTF32StringEncoding];
// You may remove the next 3 lines if you use ARC
#if ! __has_feature(objc_arc)
[string autorelease];
#endif
return string;
}