为什么我会根据字符串构造或在不同环境中 运行 时获得 NSString 的不同 UTF-8 表示形式?

Why do I get different UTF-8 representations of an NSString depending on string construction or when running in different environments?

我有一些非常简单的 Objective-C 代码分配和初始化 NSString 然后获取该字符串的 UTF-8 const char * 表示如下:

const char *s = [[[NSString alloc] initWithFormat:@"%s", "£"] UTF8String];

然后我使用以下代码打印出组成该字符串的代码单元的十六进制值:

while(*s)
    printf("%02x ", (unsigned int) *s++);

我得到以下输出:

ffffffc2 ffffffac ffffffc2 ffffffa3 

这是出乎意料的,因为我假设我只是得到 ffffffc2 ffffffa3,因为 £ 字符由两个代码单元组成,以十六进制表示为 c2然后是 a3,你可以 see here

这是在我的笔记本电脑上 运行 在本地可以想象到的最简单的 iOS 应用程序中输出的屏幕截图:

请注意,如果我按如下方式创建 NSString,输出是相同的:

[[NSString alloc] initWithFormat:@"%s", "\xc2\xa3"]

如果我改为使用 NSString 作为要插入到格式字符串中的参数,那么我将得到 ffffffc2 ffffffa3:

的预期输出
[[NSString alloc] initWithFormat:@"%@", @"£"]

更让我感到奇怪的是,与我上面(第一个版本)完全相同的 失败 代码似乎可以像我期望的那样在网上运行 Objective C codepen-type 我找到的网站,你可以 see here.

为什么当我使用代码的 initWithFormat:@"%s" 版本时,额外的代码单元被添加到字符串的 UTF-8 表示中,而且似乎只有当我在我的机器上使用 运行 时?

C 语言没有指定字符串的编码,而是指定了一组必须包含在源字符集 中的字符,并且每个字符都是一个字节。

在编译 (Objective-)C 时,Apple Clang 编译器似乎遵循这一点,C 字符串中字符的编码基于源文件的编码。源文件的默认编码是 UTF-8,因此 C 字符串文字 "£" 存储为字节 c2a300 是 UTF-8 编码“£”和一个空字节。

正如@Wileke 所说,%s 字符串格式根据系统默认编码 (documentation) 解释其参数。此默认编码似乎是 MacOSRoman,因为编码字节 c2 是字符“¬”,字节 a3 是字符“£”,因此您从 [=17= 生成的字符串]里面有这两个字符。

正如您在评论中所建议的那样,您可以使用 initWithUTF8String:, which will work provided your source file encoding is UTF-8. If your source file uses a different encoding you should instead use initWithCString:encoding: 并指定源文件的编码来解决您的问题。

如果您不确定源文件编码 select Xcode 中的文件并查看检查窗格,您可以在其中查看和更改(重新解释或转换现有字节)编码。

注意:如果在您的实际代码中,C 字符串不是由同一文件中的字符串文字形成的,您将必须确定该字符串的编码。

HTH