将UTF-8编码的NSData转换为NSString


567

我有NSData从Windows Server 编码的UTF-8 ,我想将其转换NSString为iPhone。由于数据包含两个平台上具有不同值的字符(如度数符号),如何将数据转换为字符串?


16
UTF-8在任何地方都是UTF-8。一旦是UTF-8,不同平台就没有不同的价值。这就是重点。
gnasher729 2014年

Answers:


1155

如果数据不是以Null结尾的,则应使用 -initWithData:encoding:

NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];

如果数据以空值结尾,则应改用-stringWithUTF8String:避免最后出现多余\0的数据。

NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];

(请注意,如果输入未正确使用UTF-8编码,则将得到nil。)


迅捷变体:

let newStr = String(data: data, encoding: .utf8)
// note that `newStr` is a `String?`, not a `String`.

如果数据以空值终止,则可以采用删除该空字符的安全方法,也可以采用类似于上述Objective-C版本的不安全方法。

// safe way, provided data is \0-terminated
let newStr1 = String(data: data.subdata(in: 0 ..< data.count - 1), encoding: .utf8)
// unsafe way, provided data is \0-terminated
let newStr2 = data.withUnsafeBytes(String.init(utf8String:))

5
小心!!如果使用stringWithUTF8String,请不要向其传递NULL参数,否则会引发异常
JasonZ,2012年

31
注意:在非以空值结尾的字符串上使用“ stringWithUTF8String:”时,结果是不可预测的!
Berik

2
两种解决方案都对我没有回报。
Husyn

1
您如何知道您的NSData是否为空终止?请参阅Tom Harrington的答案:stackoverflow.com/questions/27935054/…。以我的经验,永远不要以为NSData是终止为null的:它可以一次传输到另一次传输,甚至与已知服务器不同。
Elise van Looij

1
@ElisevanLooij感谢您的链接。我认为,如果所传输的数据可以随机终止,或者协议定义不正确。
kennytm

28

您可以调用此方法

+(id)stringWithUTF8String:(const char *)bytes.

27
仅当数据以空值终止时。它可能不是(实际上不是)。
IvanVučica13年

我不知道为什么在地球上这会在非空终止的字符串上中断,看看它怎么NSData知道有多少字节...
Claudiu 2013年

5
@Claudiu,您没有传递NSData对象,而是传递了它通过[data bytes]获得的(const char *),它只是一个指针,没有大小信息。因此,它指向的数据块必须为空终止。查看文档,它如此明确地说明。
jbat100

1
@ jbat100:当然可以。我不清楚。我的意思是,鉴于可以从非空终止符NSData转换为NSString(请参阅KennyTM的答案),我很惊讶没有一种+(id)stringWithUTF8Data:(NSData *)data方法可以正常工作。
克劳迪(Clauduu)2013年

stringWithUTF8Data,因此我们大多数人创建一个NSString + Foo类别并创建方法。
William Cerniuk

19

我谦虚地提交了一个类别,以减轻这种烦恼:

@interface NSData (EasyUTF8)

// Safely decode the bytes into a UTF8 string
- (NSString *)asUTF8String;

@end

@implementation NSData (EasyUTF8)

- (NSString *)asUTF8String {
    return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];    
}

@end

(请注意,如果您不使用ARC,则需要一个ARC autorelease。)

现在而不是令人费解的冗长:

NSData *data = ...
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

你可以做:

NSData *data = ...
[data asUTF8String];

18

从String到Data再回到String的Swift版本:

Xcode 10.1•Swift 4.2.1

extension Data {
    var string: String? {
        return String(data: self, encoding: .utf8)
    }
}

extension StringProtocol {
    var data: Data {
        return Data(utf8)
    }
}

extension String {
    var base64Decoded: Data? {
        return Data(base64Encoded: self)
    }
}

操场

let string = "Hello World"                                  // "Hello World"
let stringData = string.data                                // 11 bytes
let base64EncodedString = stringData.base64EncodedString()  // "SGVsbG8gV29ybGQ="
let stringFromData = stringData.string                      // "Hello World"

let base64String = "SGVsbG8gV29ybGQ="
if let data = base64String.base64Decoded {
    print(data)                                    //  11 bytes
    print(data.base64EncodedString())              // "SGVsbG8gV29ybGQ="
    print(data.string ?? "nil")                    // "Hello World"
}

let stringWithAccent = "Olá Mundo"                          // "Olá Mundo"
print(stringWithAccent.count)                               // "9"
let stringWithAccentData = stringWithAccent.data            // "10 bytes" note: an extra byte for the acute accent
let stringWithAccentFromData = stringWithAccentData.string  // "Olá Mundo\n"

16

有时,其他答案中的方法不起作用。就我而言,我正在使用RSA私钥生成签名,结果是NSData。我发现这似乎可行:

目标C

NSData *signature;
NSString *signatureString = [signature base64EncodedStringWithOptions:0];

迅速

let signatureString = signature.base64EncodedStringWithOptions(nil)

如何获取该字符串到nsdata?
Darshan Kunjadiya

1
@DarshanKunjadiya:Objective-C[[NSData alloc] initWithBase64EncodedString:signatureString options:0]斯威夫特NSData(base64EncodedString: str options: nil)
mikeho'2

1

总结一下,这是一个对我有用的完整答案。

我的问题是当我使用

[NSString stringWithUTF8String:(char *)data.bytes];

我得到的字符串是不可预测的:大约70%的字符串确实包含期望值,但是它常常导致Null甚至更糟的结果:在字符串末尾乱码。

经过一些挖掘,我切换到

[[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];

并每次都能得到预期的结果。


了解<i>为什么</ i>获得“垃圾”结果很重要。
Edgar Aroutiounian

1

使用Swift 5,您可以使用Stringinit(data:encoding:)初始值设定项,以使用UTF-8 将Data实例转换为String实例。init(data:encoding:)具有以下声明:

init?(data: Data, encoding: String.Encoding)

String通过使用给定的编码将给定的数据转换为Unicode字符来返回一个初始化值。

以下Playground代码显示了如何使用它:

import Foundation

let json = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""

let data = json.data(using: String.Encoding.utf8)!

let optionalString = String(data: data, encoding: String.Encoding.utf8)
print(String(describing: optionalString))

/*
 prints:
 Optional("{\n\"firstName\" : \"John\",\n\"lastName\" : \"Doe\"\n}")
*/
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.