如何将std::string转换为NSString?

112
我试图将标准的 `std::string` 转换为 `NSString`,但是我没有成功。
我可以使用以下代码成功地将 `NSString` 转换为 `std::string`。
NSString *realm = @"Hollywood";
std::string REALM = [realm cStringUsingEncoding:[NSString defaultCStringEncoding]];

但是当我尝试以下操作时,出现编译时错误

NSString *errorMessage = [NSString stringWithCString:REALM encoding:[NSString defaultCStringEncoding]];

我得到的错误信息是

Cannot convert 'std::string' to 'const char*' in argument passing

我这里漏掉了什么吗?


5
可能是打字错误,但是在 'NSString *realm = "Hollywood";' 这一行中,你缺少了字符串字面量的 '@' 符号。 - Vladimir
6个回答

125

如何从std::string中获取C字符串以进行转换:

NSString *errorMessage = [NSString stringWithCString:REALM.c_str() 
                                   encoding:[NSString defaultCStringEncoding]];

这似乎不是一个好的答案,因为文档中说: /* 用户相关编码,其值源自用户的默认语言和其他潜在因素。当解释具有未知编码的用户文档时,在缺少其他提示的情况下,可能需要使用此编码。如果可以的话,应该很少使用此编码。请注意,这里的一些潜在值甚至可能导致具有双向编码的标点符号等相当简单的NSString内容的意外编码转换。*/ - cyrilchampier
36
由于 std::string 很可能来自您自己的代码,而且很可能是 UTF8 编码,因此使用 [NSString stringWithUTF8String:mystring.c_str()] 更加合适。 - cyrilchampier
你们知道为什么在这里 (https://developer.apple.com/documentation/foundation/nsstring/1412128-initwithutf8string) 上说 “返回的对象可能与原始接收器不同” 吗?这是什么意思,我们如何知道它是否不同? - isJulian00
@cyrilchapier 如果我们的.mm文件中写着“未指定特定编码”会怎么样? - isJulian00

58

首先,为了使这个方法生效,你必须使用 Objective-C++;最简单的方法是将所有 *.m 文件重命名为 *.mm

迄今为止最可用(未弃用)手动将 C++ 的 std::string 转换成 NSString 的方法是:

std::string param; // <-- input
NSString* result = [NSString stringWithUTF8String:param.c_str()];
NSString* alternative = [[NSString alloc] initWithUTF8String:param.c_str()];

在大多数情况下,这将起作用 - 如果你不需要进行特定的编码检测和转换,那么UTF-8会为你提供一个良好的结果,使非拉丁字符“正常工作”。

然而,如果你正在制作一个更大的应用程序,或者你不是唯一在其中工作的人 - 你可能希望有一些更容易应用的东西。

改编自 cocoa-dev邮件列表档案

@interface NSString (cppstring_additions)
+(NSString*) stringWithwstring:(const std::wstring&)string;
+(NSString*) stringWithstring:(const std::string&)string;
-(std::wstring) getwstring;
-(std::string) getstring;
@end

@implementation NSString (cppstring_additions)

#if TARGET_RT_BIG_ENDIAN
const NSStringEncoding kEncoding_wchar_t = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32BE);
#else
const NSStringEncoding kEncoding_wchar_t = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32LE);
#endif

+(NSString*) stringWithwstring:(const std::wstring&)ws
{
    char* data = (char*)ws.data();
    unsigned size = ws.size() * sizeof(wchar_t);

    NSString* result = [[NSString alloc] initWithBytes:data length:size encoding:kEncoding_wchar_t];
    return result;
}
+(NSString*) stringWithstring:(const std::string&)s
{
    NSString* result = [[NSString alloc] initWithUTF8String:s.c_str()];
    return result;
}

-(std::wstring) getwstring
{
    NSData* asData = [self dataUsingEncoding:kEncoding_wchar_t];
    return std::wstring((wchar_t*)[asData bytes], [asData length] / sizeof(wchar_t));
}
-(std::string) getstring
{
    return [self UTF8String];
}

@end

有了这个设置(并适当地进行了#import),现在您可以:

NSString* result = [NSString stringWithstring:param];
string convertedBack = [result getstring];

对于std::wstring同样适用,这非常方便。


1
这个很棒,我在几个 iPhone 项目中都用过它。但有一件事情我注意到了,如果我正在运行苹果的单元测试框架,并且我正在测试一个使用这些方法的库,那么我必须将包含字符串转换方法的文件作为“编译源”之一包含在单元测试的“构建阶段”中。很奇怪。 - David
1
是的,单元测试本质上就像一个小程序,需要访问相同的代码。此外,为编写测试而鼓掌;-) - rvalue
1
你们知道为什么在这里(https://developer.apple.com/documentation/foundation/nsstring/1412128-initwithutf8string)上面写着“返回的对象可能与原始接收器不同”吗?这是什么意思,我们如何知道它是否不同? - isJulian00
1
@izzyMachado,文档中的这种语言通常意味着它们在接口合同下保留解析、清理或规范化输入字符串的“权利”。即它可能不会回传并仍然比较==,而是最接近或最好的表示。在这种情况下,接收者是NSString类实现,返回值不是Objective-C对象,因此他们也可能用一些标准语言来覆盖它。 - rvalue
你知道为什么当我们使用initWithUTF8String并且字符串包含非utf8字符时,它仍然能够使用该字符串(如果它是mint UTF-8)并在到达NSString后将其打印出来吗? - isJulian00
我不知道,但它很可能取决于非UTF8字节是什么,以及它们是否也是有效的UTF代码点。无论原因如何,我相信好的代码不会依赖于此;并且应该首先检测正确的编码 - 但这实际上是另一个问题。 - rvalue

26
NSString* mystring = [NSString stringWithUTF8String:stdstring.c_str()];

这会将NSString转换为UTF8吗?或者NSString使用哪种编码? - isJulian00

18

苹果现在有一种新的方法要求您进行此转换。在 XCode7 中,我使用“编辑 > 转换 > 到现代 Objective C 语法...”选项来了解这一点。它使用一个简写的 @ 符号。

std::string sCPPString = "Hello World!";
NSString *sAppleString = @(sCPPString.c_str());

抱歉如果这是一个愚蠢的问题,但只是为了确认一下,将sCPPString.c_str()复制到sAppleString中是否有保证?我的意思是,如果sCPPString超出范围,sAppleString内部的内存仍然有效吗?还有一个关于stringWithUTF8String和stringWithCString方法的问题,第一个方法是否会复制,而另一个方法只是引用内存缓冲区,或者两个方法都有保证也会进行复制?(我知道第一个方法会复制,但另一个方法呢?)我只是想确认一下。谢谢... - MathNerd

4

以下是代码片段/示例:

string str_simple = "HELLO WORLD";

//string to NSString
NSString *stringinObjC = [NSString stringWithCString:str_simple.c_str()
                                encoding:[NSString defaultCStringEncoding]];            
NSLog(stringinObjC);

4
我也发现:

NSString *nsString = [NSString stringWithFormat:@"%s",standardString];

表现出色。

4
这相当不安全并且不能保证有效。如果standardString是一个std::string,那么它是一个C++对象。C++对象传递给可变参数函数是不安全的(在C++11中,可变参数模板解决了这个问题)。如果它可以工作,那只是偶然的,几乎每个编译器都会至少警告你不要这样做(如果一开始它不是一个错误的话)。 - Vitali

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