为什么我会根据字符串构造或在不同环境中 运行 时获得 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 字符串文字 "£"
存储为字节 c2
、a3
、00
是 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
我有一些非常简单的 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 字符串文字 "£"
存储为字节 c2
、a3
、00
是 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