Convierta NSData codificado UTF-8 a NSString


567

Tengo UTF-8 codificado NSDatadesde el servidor de Windows y quiero convertirlo NSStringpara iPhone. Dado que los datos contienen caracteres (como un símbolo de grado) que tienen valores diferentes en ambas plataformas, ¿cómo convierto los datos en cadenas?


16
UTF-8 es UTF-8 en todas partes. Una vez que es UTF-8, no hay valores diferentes para diferentes plataformas. De eso se trata.
gnasher729

Respuestas:


1155

Si los datos no tienen terminación nula, debe usar -initWithData:encoding:

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

Si los datos están terminados en nulo, debería utilizarlos -stringWithUTF8String:para evitar el extra \0al final.

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

(Tenga en cuenta que si la entrada no está correctamente codificada en UTF-8, obtendrá nil).


Variante rápida:

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

Si los datos están terminados en nulo, puede ir de la manera segura que es eliminar ese carácter nulo, o la forma insegura similar a la versión de Objective-C anterior.

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

55
¡¡Cuidado!! si usa stringWithUTF8String, no le pase un argumento NULL o arrojará una excepción
JasonZ

31
MENTE ESTO: cuando se usa "stringWithUTF8String:" en una cadena que no tiene terminación nula, ¡el resultado es impredecible!
Berik

2
Ambas soluciones regresaron nulas para mí.
Husyn

1
¿Cómo sabe si su NSData tiene terminación nula o no? Vea la respuesta de Tom Harrington en: stackoverflow.com/questions/27935054/… . En mi experiencia, uno nunca debería suponer que NSData tiene terminación nula o no: puede diferir de una transmisión a la siguiente, incluso de un servidor conocido.
Elise van Looij

1
@ElisevanLooij Gracias por el enlace. Yo diría que si los datos transmitidos pudieran ser aleatoriamente terminados en nulo o no, el protocolo está mal definido.
kennytm

28

Podrías llamar a este método

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

27
Solo si los datos están terminados en nulo. Lo que puede no ser (y, de hecho, probablemente no lo sea).
Ivan Vučica

No sé por qué demonios esto se rompería en cadenas sin terminación nula, viendo cómo NSDatasabe cuántos bytes tiene ...
Claudiu

55
@Claudiu, no está pasando un objeto NSData, lo está pasando a (const char *) obtenido con [bytes de datos], que es solo un puntero, sin información de tamaño. Por lo tanto, el bloque de datos al que apunta debe estar terminado nulo. Consulte la documentación, lo dice explícitamente.
jbat100

1
@ jbat100: Por supuesto. No estaba claro Es decir, dado que es posible pasar de una versión sin terminación nula NSDataa una NSString(ver la respuesta de KennyTM), me sorprende que no haya una +(id)stringWithUTF8Data:(NSData *)dataque simplemente funcione.
Claudiu

stringWithUTF8Data, por lo tanto, la mayoría de nosotros creamos una categoría NSString + Foo y creamos el método.
William Cerniuk

19

Presento humildemente una categoría para que esto sea menos molesto:

@interface NSData (EasyUTF8)

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

@end

y

@implementation NSData (EasyUTF8)

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

@end

(Tenga en cuenta que si no está utilizando ARC, necesitará un autoreleaseallí).

Ahora en lugar de lo terriblemente detallado:

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

Tu puedes hacer:

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

18

La versión Swift de String a Data y de regreso 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)
    }
}

Patio de recreo

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 veces, los métodos en las otras respuestas no funcionan. En mi caso, estoy generando una firma con mi clave privada RSA y el resultado es NSData. Descubrí que esto parece funcionar:

C objetivo

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

Rápido

let signatureString = signature.base64EncodedStringWithOptions(nil)

¿Cómo llevar esa cadena a nsdata?
Darshan Kunjadiya

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

1

Solo para resumir, aquí hay una respuesta completa, que funcionó para mí.

Mi problema era que cuando usaba

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

La cadena que obtuve fue impredecible: alrededor del 70% contenía el valor esperado, pero con demasiada frecuencia resultó con Null o incluso peor: basura al final de la cadena.

Después de cavar un poco, cambié a

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

Y obtuvo el resultado esperado cada vez.


Es importante que comprenda <i> por qué </i> obtuvo resultados de "basura".
Edgar Aroutiounian

1

Con Swift 5, puede usar Stringel init(data:encoding:)inicializador para convertir una Datainstancia en una Stringinstancia usando UTF-8. init(data:encoding:)tiene la siguiente declaración:

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

Devuelve un Stringinicializado al convertir datos dados en caracteres Unicode usando una codificación dada.

El siguiente código de Playground muestra cómo 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}")
*/
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.