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 ?


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

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.

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];

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"

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)

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.


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}")
*/
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.