Chuyển đổi NSData được mã hóa UTF-8 sang NSString


567

Tôi đã mã hóa UTF-8 NSDatatừ máy chủ windows và tôi muốn chuyển đổi nó sang NSStringiPhone. Vì dữ liệu chứa các ký tự (như ký hiệu độ) có các giá trị khác nhau trên cả hai nền tảng, làm cách nào để chuyển đổi dữ liệu thành chuỗi?


16
UTF-8 là UTF-8 ở mọi nơi. Khi là UTF-8, không có giá trị khác nhau cho các nền tảng khác nhau. Đó là toàn bộ quan điểm của nó.
gnasher729

Câu trả lời:


1155

Nếu dữ liệu không được kết thúc bằng null, bạn nên sử dụng -initWithData:encoding:

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

Nếu dữ liệu bị chấm dứt null, thay vào đó bạn nên sử dụng -stringWithUTF8String:để tránh phần bổ sung \0ở cuối.

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

(Lưu ý rằng nếu đầu vào không được mã hóa UTF-8 đúng cách, bạn sẽ nhận được nil.)


Biến thể Swift:

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

Nếu dữ liệu bị chấm dứt null, bạn có thể đi theo cách an toàn là loại bỏ ký tự null đó hoặc cách không an toàn tương tự như phiên bản Objective-C ở trên.

// 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
coi chừng!! nếu sử dụng chuỗiWithUTF8String, đừng truyền cho nó một đối số NULL hoặc nó sẽ đưa ra một ngoại lệ
JasonZ

31
MIND NÀY: khi sử dụng "stringWithUTF8String:" trên một chuỗi không bị chấm dứt null, kết quả là không thể đoán trước!
Berik

2
Cả hai giải pháp trả lại cho tôi.
Husyn

1
Làm thế nào để bạn biết liệu NSData của bạn có bị chấm dứt null hay không? Xem câu trả lời của Tom Harrington tại: stackoverflow.com/questions/27935054/ . Theo kinh nghiệm của tôi, người ta không nên cho rằng NSData có thể bị chấm dứt null hay không: nó có thể khác nhau từ một lần truyền tới lần truyền tiếp theo, thậm chí từ một máy chủ đã biết.
Elise van Looij

1
@ElisevanLooij Cảm ơn đã liên kết. Tôi tranh luận rằng nếu dữ liệu truyền có thể bị chấm dứt ngẫu nhiên null hoặc giao thức không được xác định rõ ràng.
kennytm

28

Bạn có thể gọi phương thức này

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

27
Chỉ khi dữ liệu bị hủy kết thúc. Mà nó có thể không (và trên thực tế, có lẽ là không).
Ivan Vučica

tôi không biết tại sao trên trái đất, điều này sẽ phá vỡ các chuỗi không kết thúc bằng cách xem cách NSDatabiết nó có bao nhiêu byte ...
Claudiu

5
@Claudiu, bạn không truyền vào một đối tượng NSData, bạn đang truyền cho nó một (const char *) thu được bằng [byte dữ liệu], đây chỉ là một con trỏ, không có thông tin kích thước. Do đó, khối dữ liệu mà nó trỏ đến phải được kết thúc bằng null. Kiểm tra các tài liệu, nó nói rất rõ ràng.
jbat100

1
@ jbat100: Tất nhiên rồi. Tôi đã không rõ ràng. Ý tôi là, cho rằng có thể đi từ một con số không kết thúc NSDatathành một NSString(xem câu trả lời của KennyTM), tôi ngạc nhiên rằng không có +(id)stringWithUTF8Data:(NSData *)datacái nào chỉ hoạt động.
Claudiu

chuỗiWithUTF8Data, do đó hầu hết chúng ta tạo ra một thể loại NSString + Foo và tạo phương thức.
William Cerniuk

19

Tôi khiêm tốn gửi một danh mục để làm cho điều này bớt khó chịu:

@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

(Lưu ý rằng nếu bạn không sử dụng ARC, bạn sẽ cần autoreleaseđến đó.)

Bây giờ thay vì dài dòng kinh khủng:

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

Bạn có thể làm:

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

18

Phiên bản Swift từ Chuỗi sang Dữ liệu và quay lại Chuỗi:

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)
    }
}

Sân chơi

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

Đôi khi, các phương pháp trong các câu trả lời khác không hoạt động. Trong trường hợp của tôi, tôi đang tạo chữ ký bằng khóa riêng RSA của mình và kết quả là NSData. Tôi thấy rằng điều này dường như làm việc:

Mục tiêu-C

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

Nhanh

let signatureString = signature.base64EncodedStringWithOptions(nil)

Làm thế nào để có được chuỗi đó đến nsdata?
Darshan Kunjadiya

1
@DarshanKunjadiya: Mục tiêu-C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
mikeho

1

Tóm lại, đây là một câu trả lời hoàn chỉnh, có tác dụng với tôi.

Vấn đề của tôi là khi tôi sử dụng

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

Chuỗi tôi nhận được là không thể đoán trước: Khoảng 70% nó chứa giá trị mong đợi, nhưng quá thường xuyên, nó dẫn đến Nullhoặc thậm chí tệ hơn: bị cắt xén ở cuối chuỗi.

Sau khi đào xong tôi chuyển sang

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

Và nhận được kết quả mong đợi mọi lúc.


Điều quan trọng là bạn hiểu <i> tại sao </ i> bạn có kết quả 'rác'.
Edgar Aroutiounian

1

Với Swift 5, bạn có thể sử dụng String's init(data:encoding:)initializer để chuyển đổi một Dataví dụ thành một Stringví dụ sử dụng UTF-8. init(data:encoding:)có tuyên bố sau:

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

Trả về một Stringkhởi tạo bằng cách chuyển đổi dữ liệu đã cho thành các ký tự Unicode bằng cách sử dụng mã hóa đã cho.

Mã Playground sau đây cho biết cách sử dụng nó:

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}")
*/
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.