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?

Ashwini Shahapurkar
fuente
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:))
kennytm
fuente
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.
Gouldsc
fuente
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];
Claudiu
fuente
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"
Leo Dabus
fuente
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)
mikeho
fuente
¿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.

Galón
fuente
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}")
*/
Imanou Petit
fuente