Konvertieren Sie UTF-8-codierte NSData in NSString

567

Ich habe UTF-8 NSDatavom Windows-Server codiert und möchte es NSStringfür das iPhone konvertieren . Wie konvertiere ich Daten in Zeichenfolgen, da Daten Zeichen (wie ein Gradsymbol) enthalten, die auf beiden Plattformen unterschiedliche Werte haben ?

Ashwini Shahapurkar
quelle
16
UTF-8 ist überall UTF-8. Sobald es UTF-8 ist, gibt es keine unterschiedlichen Werte für verschiedene Plattformen. Das ist der springende Punkt.
Gnasher729

Antworten:

1155

Wenn die Daten nicht nullterminiert sind, sollten Sie verwenden -initWithData:encoding:

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

Wenn die Daten nullterminiert sind, sollten Sie stattdessen verwenden -stringWithUTF8String:, um das Extra \0am Ende zu vermeiden .

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

(Beachten Sie, dass Sie erhalten, wenn die Eingabe nicht ordnungsgemäß UTF-8-codiert ist nil.)


Schnelle Variante:

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

Wenn die Daten nullterminiert sind, können Sie den sicheren Weg gehen, bei dem das Nullzeichen entfernt wird, oder den unsicheren Weg, der der obigen Objective-C-Version ähnelt.

// 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
quelle
5
Achtung!! Wenn Sie stringWithUTF8String verwenden, übergeben Sie ihm kein NULL-Argument, da sonst eine Ausnahme
ausgelöst
31
Beachten Sie Folgendes: Wenn Sie "stringWithUTF8String:" für einen String verwenden, der nicht nullterminiert ist, ist das Ergebnis unvorhersehbar!
Berik
2
Beide Lösungen geben für mich nichts zurück.
Husyn
1
Woher wissen Sie, ob Ihre NSData nullterminiert sind oder nicht? Siehe Tom Harringtons Antwort unter: stackoverflow.com/questions/27935054/… . Nach meiner Erfahrung sollte man niemals davon ausgehen, dass NSData entweder nullterminiert ist oder nicht: Es kann sich von einer Übertragung zur nächsten unterscheiden, selbst von einem bekannten Server.
Elise van Looij
1
@ ElisevanLooij Danke für den Link. Ich würde argumentieren, dass das Protokoll schlecht definiert ist, wenn die übertragenen Daten zufällig nullterminiert werden könnten oder nicht.
Kennytm
28

Sie können diese Methode aufrufen

+(id)stringWithUTF8String:(const char *)bytes.
Gouldsc
quelle
27
Nur wenn die Daten nullterminiert sind. Was es vielleicht nicht ist (und wahrscheinlich auch nicht ist).
Ivan Vučica
Ich weiß nicht, warum um alles in der Welt dies bei nicht nullterminierten Zeichenfolgen brechen würde, wenn man sieht NSData, wie viele Bytes es hat ...
Claudiu
5
@Claudiu, Sie übergeben kein NSData-Objekt, sondern ein (const char *), das mit [Datenbytes] erhalten wurde. Dies ist nur ein Zeiger, keine Größeninformationen. Daher muss der Datenblock, auf den er zeigt, mit Null abgeschlossen werden. Schauen Sie sich die Dokumentation an, heißt es ausdrücklich.
jbat100
1
@ jbat100: Natürlich. Ich war nicht klar. Ich meinte, da es möglich ist, von einer nicht nullterminierten NSDatazu einer zu NSStringwechseln (siehe KennyTMs Antwort), bin ich überrascht, dass es keine gibt, +(id)stringWithUTF8Data:(NSData *)datadie einfach funktioniert.
Claudiu
stringWithUTF8Data, daher erstellen die meisten von uns eine NSString + Foo-Kategorie und erstellen die Methode.
William Cerniuk
19

Ich reiche demütig eine Kategorie ein, um dies weniger ärgerlich zu machen:

@interface NSData (EasyUTF8)

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

@end

und

@implementation NSData (EasyUTF8)

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

@end

(Beachten Sie, dass Sie autoreleasedort eine benötigen, wenn Sie ARC nicht verwenden .)

Jetzt anstelle der entsetzlich ausführlichen:

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

Du kannst tun:

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

Die Swift-Version von String zu Data und zurück zu 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)
    }
}

Spielplatz

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
quelle
16

Manchmal funktionieren die Methoden in den anderen Antworten nicht. In meinem Fall generiere ich eine Signatur mit meinem privaten RSA-Schlüssel und das Ergebnis ist NSData. Ich fand, dass dies zu funktionieren scheint:

Ziel c

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

Schnell

let signatureString = signature.base64EncodedStringWithOptions(nil)
mikeho
quelle
Wie bekomme ich diesen String zu nsdata?
Darshan Kunjadiya
1
@DarshanKunjadiya: Ziel-C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
Mikeho
1

Um es zusammenzufassen, hier ist eine vollständige Antwort, die für mich funktioniert hat.

Mein Problem war das, als ich es benutzte

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

Die Saite, die ich bekam, war unvorhersehbar: Zu etwa 70% enthielt sie den erwarteten Wert, aber zu oft führte sie zu Nulloder noch schlimmer: am Ende der Saite gekleidet.

Nach einigem Graben wechselte ich zu

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

Und bekam jedes Mal das erwartete Ergebnis.

Gal
quelle
Es ist wichtig, dass Sie verstehen, warum Sie Müll-Ergebnisse erhalten haben.
Edgar Aroutiounian
1

Mit Swift 5, können Sie String‚s init(data:encoding:)initializer, um eine konvertieren DataInstanz in eine StringInstanz UTF-8 verwenden. init(data:encoding:)hat die folgende Erklärung:

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

Gibt eine StringInitialisierung zurück, indem bestimmte Daten mit einer bestimmten Codierung in Unicode-Zeichen konvertiert werden.

Der folgende Spielplatzcode zeigt, wie man ihn benutzt:

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
quelle