Convertir NSData encodé en UTF-8 en NSString

567

J'ai encodé UTF-8 à NSDatapartir du serveur Windows et je souhaite le convertir en NSStringiPhone. Étant donné que les données contiennent des caractères (comme un symbole de degré) qui ont des valeurs différentes sur les deux plates-formes, comment convertir des données en chaîne?

Ashwini Shahapurkar
la source
16
UTF-8 est UTF-8 partout. Une fois que c'est UTF-8, il n'y a pas de valeurs différentes pour différentes plates-formes. Voilà tout l'intérêt.
gnasher729

Réponses:

1155

Si les données ne se terminent pas par un caractère nul, vous devez utiliser -initWithData:encoding:

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

Si les données se terminent par un caractère nul, vous devez plutôt utiliser -stringWithUTF8String:pour éviter le supplément \0à la fin.

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

(Notez que si l'entrée n'est pas correctement encodée en UTF-8, vous obtiendrez nil.)


Variante rapide:

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

Si les données se terminent par null, vous pouvez utiliser la méthode sûre qui consiste à supprimer le caractère nul ou la méthode non sécurisée similaire à la version Objective-C ci-dessus.

// 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
la source
5
Fais attention!! si vous utilisez stringWithUTF8String, ne lui passez pas un argument NULL ou il
lèvera
31
À L'ESPRIT: lors de l'utilisation de "stringWithUTF8String:" sur une chaîne qui n'est pas terminée par un caractère nul, le résultat est imprévisible!
Berik
2
Les deux solutions me sont retournées nulles.
Husyn
1
Comment savez-vous si votre NSData se termine par null ou non? Voir la réponse de Tom Harrington à: stackoverflow.com/questions/27935054/… . D'après mon expérience, il ne faut jamais supposer que NSData se termine par null ou non: il peut différer d'une transmission à l'autre, même d'un serveur connu.
Elise van Looij
1
@ElisevanLooij Merci pour le lien. Je dirais que si les données transmises peuvent être terminées de façon aléatoire ou non, le protocole est mal défini.
kennytm
28

Vous pourriez appeler cette méthode

+(id)stringWithUTF8String:(const char *)bytes.
Gouldsc
la source
27
Uniquement si les données sont terminées par null. Ce qui n'est peut-être pas (et, en fait, ce n'est probablement pas le cas).
Ivan Vučica
je ne sais pas pourquoi diable cela se briserait sur les chaînes non terminées en voyant comment le NSDatasait combien d'octets il a ...
Claudiu
5
@Claudiu, vous ne passez pas dans un objet NSData, vous lui passez un (const char *) obtenu avec [octets de données], qui est juste un pointeur, aucune information de taille. Par conséquent, le bloc de données vers lequel il pointe doit être terminé par null. Consultez la documentation, il le dit explicitement.
jbat100
1
@ jbat100: Bien sûr. Je n'étais pas clair. Je voulais dire, étant donné qu'il est possible de passer d'une terminaison non nulle NSDataà une NSString(voir la réponse de KennyTM), je suis surpris qu'il n'y en ait pas +(id)stringWithUTF8Data:(NSData *)dataqui fonctionne.
Claudiu
stringWithUTF8Data, donc la plupart d'entre nous créons une catégorie NSString + Foo et créent la méthode.
William Cerniuk du
19

Je soumets humblement une catégorie pour rendre cela moins ennuyeux:

@interface NSData (EasyUTF8)

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

@end

et

@implementation NSData (EasyUTF8)

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

@end

(Notez que si vous n'utilisez pas ARC, vous en aurez besoin autorelease.)

Maintenant, au lieu de ce qui est terriblement verbeux:

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

Tu peux faire:

NSData *data = ...
[data asUTF8String];
Claudiu
la source
18

La version Swift de String en Data et de retour en 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)
    }
}

Terrain de jeux

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
la source
16

Parfois, les méthodes des autres réponses ne fonctionnent pas. Dans mon cas, je génère une signature avec ma clé privée RSA et le résultat est NSData. J'ai trouvé que cela semble fonctionner:

Objectif c

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

Rapide

let signatureString = signature.base64EncodedStringWithOptions(nil)
mikeho
la source
comment obtenir cette chaîne en nsdata?
Darshan Kunjadiya
1
@DarshanKunjadiya: Objective-C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
mikeho
1

Juste pour résumer, voici une réponse complète, qui a fonctionné pour moi.

Mon problème était que lorsque j'utilisais

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

La chaîne que j'ai obtenue était imprévisible: autour de 70%, elle contenait la valeur attendue, mais trop souvent elle a résulté avec Nullou pire encore: endommagée à la fin de la chaîne.

Après avoir creusé, je suis passé à

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

Et a obtenu le résultat attendu à chaque fois.

Fille
la source
Il est important que vous compreniez <i> pourquoi </i> vous avez obtenu des résultats 'poubelles'.
Edgar Aroutiounian
1

Avec Swift 5, vous pouvez utiliser Stringl' init(data:encoding:)initialiseur de pour convertir une Datainstance en une Stringinstance utilisant UTF-8. init(data:encoding:)a la déclaration suivante:

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

Renvoie un Stringinitialisé en convertissant des données données en caractères Unicode en utilisant un encodage donné.

Le code Playground suivant montre comment l'utiliser:

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
la source