Converti NSData codificato UTF-8 in NSString


567

Ho UTF-8 codificato NSDatada Windows Server e voglio convertirlo in NSStringper iPhone. Poiché i dati contengono caratteri (come un simbolo di grado) che hanno valori diversi su entrambe le piattaforme, come posso convertire i dati in stringa?


16
UTF-8 è UTF-8 ovunque. Una volta che è UTF-8, non ci sono valori diversi per piattaforme diverse. Questo è il punto.
gnasher729,

Risposte:


1155

Se i dati non sono null, è necessario utilizzare -initWithData:encoding:

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

Se i dati sono nulli, è necessario utilizzare invece -stringWithUTF8String:per evitare il extra \0alla fine.

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

(Nota che se l'ingresso non è correttamente codificato UTF-8, otterrai nil.)


Variante rapida:

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

Se i dati sono terminati con null, è possibile procedere nel modo sicuro che è rimuovere quel carattere null o nel modo non sicuro simile alla versione Objective-C sopra.

// 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
attento!! se si utilizza stringWithUTF8String, non passare un argomento NULL o genererà un'eccezione
JasonZ,

31
RICORDA QUESTO: quando si utilizza "stringWithUTF8String:" su una stringa che non ha terminazione nulla, il risultato è imprevedibile!
Berik,

2
Entrambe le soluzioni restituiscono zero per me.
Husyn,

1
Come fai a sapere se il tuo NSData è nullo o no? Vedi la risposta di Tom Harrington su: stackoverflow.com/questions/27935054/… . Nella mia esperienza, non si dovrebbe mai supporre che NSData sia o null-terminato oppure no: può differire da una trasmissione alla successiva, anche da un server noto.
Elise van Looij,

1
@ElisevanLooij Grazie per il link. Direi che se i dati trasmessi potessero essere casualmente nulli o meno il protocollo sarebbe mal definito.
kennytm,

28

Potresti chiamare questo metodo

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

27
Solo se i dati sono nulli. Quale potrebbe non essere (e, in effetti, probabilmente non lo è).
Ivan Vučica,

non so perché mai questo si spezzerebbe su stringhe senza terminazione nulla vedendo come NSDatasa quanti byte ha ...
Claudiu

5
@Claudiu, non stai passando un oggetto NSData, stai passando un (const char *) ottenuto con [byte di dati], che è solo un puntatore, nessuna informazione sulla dimensione. Quindi il blocco dati a cui punta deve essere nullo. Controlla la documentazione, dice così esplicitamente.
jbat100,

1
@ jbat100: certo. Non ero chiaro. Volevo dire, dato che è possibile passare da un non-null-terminato NSDataa un NSString(vedi la risposta di KennyTM), sono sorpreso che non ce ne sia uno +(id)stringWithUTF8Data:(NSData *)datache funzioni .
Claudiu,

stringWithUTF8Data, quindi la maggior parte di noi crea una categoria NSString + Foo e crea il metodo.
William Cerniuk,

19

Umilmente invio una categoria per rendere questo meno fastidioso:

@interface NSData (EasyUTF8)

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

@end

e

@implementation NSData (EasyUTF8)

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

@end

(Nota che se non usi ARC avrai bisogno di un autoreleaselì.)

Ora invece del terribilmente prolisso:

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

Tu puoi fare:

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

18

La versione Swift da String a Data e di nuovo a 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)
    }
}

Terreno di gioco

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

A volte, i metodi nelle altre risposte non funzionano. Nel mio caso, sto generando una firma con la mia chiave privata RSA e il risultato è NSData. Ho scoperto che questo sembra funzionare:

Objective-C

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

veloce

let signatureString = signature.base64EncodedStringWithOptions(nil)

come ottenere quella stringa in nsdata?
Darshan Kunjadiya,

1
@DarshanKunjadiya: Objective-C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
mikeho, l'

1

Per riassumere, ecco una risposta completa, che ha funzionato per me.

Il mio problema era che quando l'ho usato

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

La stringa che ho ricevuto era imprevedibile: circa il 70% conteneva il valore atteso, ma troppo spesso risultava con Nullo anche peggio: confonduto alla fine della stringa.

Dopo alcuni scavi sono passato a

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

E ho ottenuto il risultato atteso ogni volta.


È importante capire <i> perché </i> hai ottenuto risultati "spazzatura".
Edgar Aroutiounian,

1

Con Swift 5, puoi utilizzare Stringl' init(data:encoding:)inizializzatore per convertire Dataun'istanza in Stringun'istanza utilizzando UTF-8. init(data:encoding:)ha la seguente dichiarazione:

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

Restituisce un Stringinizializzato convertendo i dati dati in caratteri Unicode utilizzando una determinata codifica.

Il seguente codice Playground mostra come usarlo:

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}")
*/
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.