Konversi UTF-8 yang disandikan NSData ke NSString

567

Saya memiliki UTF-8 yang dikodekan NSData dari server windows dan saya ingin mengubahnya menjadi NSStringuntuk iPhone. Karena data berisi karakter (seperti simbol derajat) yang memiliki nilai berbeda di kedua platform, bagaimana cara mengonversi data menjadi string?

Ashwini Shahapurkar
sumber
16
UTF-8 adalah UTF-8 di mana-mana. Setelah itu UTF-8, tidak ada nilai yang berbeda untuk platform yang berbeda. Itulah intinya.
gnasher729

Jawaban:

1155

Jika data tidak diakhiri null, Anda harus menggunakan -initWithData:encoding:

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

Jika data diakhiri secara null, Anda sebaiknya menggunakan -stringWithUTF8String:untuk menghindari ekstra \0di bagian akhir.

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

(Perhatikan bahwa jika inputnya tidak benar-dikodekan UTF-8, Anda akan mendapatkan nil .)


Varian cepat:

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

Jika data diakhiri dengan nol, Anda bisa menggunakan cara aman yang menghapus karakter nol itu, atau cara tidak aman mirip dengan versi Objective-C di atas.

// 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:))
kennytm
sumber
5
Awas!! jika menggunakan stringWithUTF8String, jangan berikan argumen NULL atau itu akan melempar pengecualian
JasonZ
31
PIKIRAN INI: ketika menggunakan "stringWithUTF8String:" pada string yang tidak diakhiri null, hasilnya tidak dapat diprediksi!
Berik
2
Kedua solusi mengembalikan nol untuk saya.
Husyn
1
Bagaimana Anda tahu apakah NSData Anda diakhiri null atau tidak? Lihat jawaban Tom Harrington di: stackoverflow.com/questions/27935054/… . Dalam pengalaman saya, orang tidak boleh berasumsi NSData adalah null-dihentikan atau tidak: dapat berbeda dari satu transmisi ke yang berikutnya, bahkan dari server yang dikenal.
Elise van Looij
1
@ ElisevanLooij Terima kasih atas tautannya. Saya berpendapat bahwa jika data yang dikirim dapat diakhiri secara acak atau tidak protokolnya tidak jelas.
kennytm
28

Anda dapat memanggil metode ini

+(id)stringWithUTF8String:(const char *)bytes.
Gouldsc
sumber
27
Hanya jika data diakhiri null. Yang mungkin tidak (dan, pada kenyataannya, mungkin tidak).
Ivan Vučica
saya tidak tahu mengapa di bumi ini akan terputus pada string non-null-dihentikan melihat bagaimana NSDatatahu berapa banyak byte yang dimilikinya ...
Claudiu
5
@Claudiu, Anda tidak meneruskan objek NSData, Anda memberikannya (const char *) yang diperoleh dengan [data byte], yang hanya berupa pointer, tanpa informasi ukuran. Oleh karena itu blok data yang ditunjukkannya harus diakhiri nol. Lihat dokumentasi, katanya secara eksplisit.
jbat100
1
@ jbat100: Tentu saja. Saya tidak jelas. Maksud saya, mengingat bahwa mungkin untuk beralih dari non-null-dihentikan NSDatake NSString(lihat jawaban KennyTM), saya terkejut tidak ada +(id)stringWithUTF8Data:(NSData *)datayang hanya berfungsi.
Claudiu
stringWithUTF8Data, maka kebanyakan dari kita membuat kategori NSString + Foo dan membuat metode.
William Cerniuk
19

Dengan rendah hati saya mengirimkan kategori untuk membuat ini tidak terlalu mengganggu:

@interface NSData (EasyUTF8)

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

@end

dan

@implementation NSData (EasyUTF8)

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

@end

(Perhatikan bahwa jika Anda tidak menggunakan ARC, Anda memerlukannya autorelease.)

Sekarang alih-alih kata kerja yang mengerikan:

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

Anda dapat melakukan:

NSData *data = ...
[data asUTF8String];
Claudiu
sumber
18

Versi Swift dari String ke Data dan kembali ke String:

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

Tempat bermain

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"
Leo Dabus
sumber
16

Terkadang, metode dalam jawaban lain tidak bekerja. Dalam kasus saya, saya membuat tanda tangan dengan kunci pribadi RSA saya dan hasilnya adalah NSData. Saya menemukan bahwa ini tampaknya berhasil:

Objektif-C

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

Cepat

let signatureString = signature.base64EncodedStringWithOptions(nil)
mikeho
sumber
bagaimana cara mendapatkan string itu ke nsdata?
Darshan Kunjadiya
1
@DarshanKunjadiya: Objective-C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
mikeho
1

Singkatnya, inilah jawaban yang lengkap, yang bekerja untuk saya.

Masalah saya adalah ketika saya menggunakannya

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

String yang saya dapat tidak dapat diprediksi: Sekitar 70% itu memang mengandung nilai yang diharapkan, tetapi terlalu sering hasilnya Null atau bahkan lebih buruk: rusak di akhir string.

Setelah menggali saya beralih ke

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

Dan mendapat hasil yang diharapkan setiap saat.

Gal
sumber
Penting bagi Anda untuk memahami <i> mengapa </i> Anda mendapat hasil 'sampah'.
Edgar Aroutiounian
1

Dengan Swift 5, Anda dapat menggunakan String's init(data:encoding:)initializer untuk mengkonversi Datacontoh menjadi Stringcontoh menggunakan UTF-8. init(data:encoding:)memiliki deklarasi berikut:

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

Mengembalikan yang Stringdiinisialisasi dengan mengubah data yang diberikan menjadi karakter Unicode menggunakan pengkodean yang diberikan.

Kode Playground berikut menunjukkan cara menggunakannya:

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}")
*/
Imanou Petit
sumber