Konwertuj NSData zakodowane w UTF-8 na NSString


567

Mam kodowanie UTF-8 NSDataz serwera Windows i chcę przekonwertować go NSStringna iPhone'a. Ponieważ dane zawierają znaki (takie jak symbol stopnia), które mają różne wartości na obu platformach, w jaki sposób przekonwertować dane na ciąg?


16
UTF-8 jest wszędzie UTF-8. Gdy jest to UTF-8, nie ma różnych wartości dla różnych platform. Właśnie o to chodzi.
— gnasher729

Odpowiedzi:


1155

Jeśli dane nie są zakończone zerem, należy użyć -initWithData:encoding:

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

Jeśli dane są zakończone zerem, powinieneś zamiast tego użyć, -stringWithUTF8String:aby uniknąć dodatkowych \0na końcu.

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

(Zauważ, że jeśli wejście nie jest poprawnie zakodowane w UTF-8, otrzymasz nil.)


Szybki wariant:

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

Jeśli dane są zakończone zerem, możesz przejść bezpieczną drogą, która polega na usunięciu znaku null lub niebezpieczną drogą podobną do powyższej wersji Objective-C.

// 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
Uważaj!! jeśli używasz stringWithUTF8String, nie przekazuj mu argumentu NULL, w przeciwnym razie
— wygeneruje

31
UWAŻAJ, ŻE: w przypadku użycia ciągu „stringWithUTF8String:” na łańcuchu, który nie jest zakończony zerem, wynik jest nieprzewidywalny!
— Berik

2
Oba rozwiązania zwracają mi zero.
— Husyn

1
Skąd wiesz, czy twoje NSData jest zakończone zerowo, czy nie? Patrz odpowiedź Tom Harrington w: stackoverflow.com/questions/27935054/... . Z mojego doświadczenia wynika, że ​​nigdy nie należy zakładać, że NSData ma albo zerowe zakończenie, albo nie: może różnić się w zależności od transmisji, nawet od znanego serwera.
— Elise van Looij

1
@ElisevanLooij Dzięki za link. Twierdziłbym, że jeśli przesyłane dane mogłyby zostać losowo zakończone zerem lub nie, protokół jest źle zdefiniowany.
— kennytm

28

Możesz wywołać tę metodę

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

27
Tylko jeśli dane są zakończone zerem. Który może nie być (a tak naprawdę prawdopodobnie nie jest).
— Ivan Vučica,

nie wiem, dlaczego, do licha, miałoby to załamać się na łańcuchach nie zakończonych zerem, widząc, skąd NSDatawie, ile ma bajtów ...
— Claudiu

5
@ Claudiu, nie przekazujesz obiektu NSData, przekazujesz go (const char *) uzyskany z [bajtów danych], który jest tylko wskaźnikiem, bez informacji o rozmiarze. Dlatego wskazany blok danych musi być zakończony zerem. Sprawdź dokumentację, mówi tak wyraźnie.
— jbat100

1
@ jbat100: Oczywiście. Nie byłem jasny. Miałem na myśli, biorąc pod uwagę, że możliwe jest przejście z opcji z zerowym zakończeniem NSDatana NSString(patrz odpowiedź Kenny'ego), jestem zaskoczony, że nie ma czegoś, +(id)stringWithUTF8Data:(NSData *)dataco po prostu działa.
— Claudiu

stringWithUTF8Data, dlatego większość z nas tworzy kategorię NSString + Foo i tworzy metodę.
— William Cerniuk

19

Pokornie przesyłam kategorię, aby było to mniej denerwujące:

@interface NSData (EasyUTF8)

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

@end

i

@implementation NSData (EasyUTF8)

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

@end

(Pamiętaj, że jeśli nie używasz ARC, potrzebujesz autorelease .)

Teraz zamiast przerażająco gadatliwego:

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

Możesz to zrobić:

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

18

Wersja Swift od String do Data iz powrotem do 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)
    }
}

Plac zabaw

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

Czasami metody z innych odpowiedzi nie działają. W moim przypadku generuję podpis za pomocą mojego prywatnego klucza RSA, a wynikiem jest NSData. Odkryłem, że to działa:

Cel C

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

Szybki

let signatureString = signature.base64EncodedStringWithOptions(nil)

jak zdobyć ten ciąg do nsdata?
— Darshan Kunjadiya,

1
@DarshanKunjadiya: Cel C : [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift : NSData(base64EncodedString: str options: nil)
— mikeho,

1

Podsumowując, oto pełna odpowiedź, która zadziałała dla mnie.

Mój problem polegał na tym, że kiedy używałem

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

Ciąg, który otrzymałem, był nieprzewidywalny: około 70% zawierało oczekiwaną wartość, ale zbyt często to powodowało Null a nawet gorzej: garbowany na końcu łańcucha.

Po kopaniu przełączyłem się na

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

I za każdym razem uzyskałem oczekiwany wynik.


Ważne jest, aby zrozumieć <i> dlaczego </i> wyniki „śmieci”.
— Edgar Aroutiounian

1

Swift z 5, można użyć String„s init(data:encoding:)inicjator w celu przekształcenia Datainstancji do Stringinstancji przy użyciu UTF-8. init(data:encoding:)ma następującą deklarację:

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

Zwraca Stringzainicjowany przez konwersję danych na znaki Unicode przy użyciu danego kodowania.

Poniższy kod placu zabaw pokazuje, jak go używać:

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}")
*/
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.