iphone notifications nsdata

iphone - La mejor manera de serializar un NSData en una cadena hexadeximal



notifications (15)

Versión funcional Swift

Un trazador de líneas:

let hexString = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length).map { String(format: "%02x", $0) }.joinWithSeparator("")

Aquí hay un formulario de extensión que se puede reutilizar y autoeducar:

extension NSData { func base16EncodedString(uppercase uppercase: Bool = false) -> String { let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes), count: self.length) let hexFormat = uppercase ? "X" : "x" let formatString = "%02/(hexFormat)" let bytesAsHexStrings = buffer.map { String(format: formatString, $0) } return bytesAsHexStrings.joinWithSeparator("") } }

Alternativamente, utilice reduce("", combine: +) lugar de joinWithSeparator("") para que sus pares lo vean como un maestro funcional.

Editar: Cambié String ($ 0, radix: 16) a String (formato: "% 02x", $ 0), porque los números de un dígito necesitan tener un relleno cero

Estoy buscando una manera agradable de serializar un objeto NSData en una cadena hexadecimal. La idea es serializar el dispositivo Token utilizado para la notificación antes de enviarlo a mi servidor.

Tengo la siguiente implementación, pero estoy pensando que debe haber una forma más corta y más agradable de hacerlo.

+ (NSString*) serializeDeviceToken:(NSData*) deviceToken { NSMutableString *str = [NSMutableString stringWithCapacity:64]; int length = [deviceToken length]; char *bytes = malloc(sizeof(char) * length); [deviceToken getBytes:bytes length:length]; for (int i = 0; i < length; i++) { [str appendFormat:@"%02.2hhX", bytes[i]]; } free(bytes); return str; }


Aquí hay un método de categoría NSData altamente optimizado para generar una cadena hexadecimal. Mientras que la respuesta de @Dave Gallagher es suficiente para un tamaño relativamente pequeño, la memoria y el rendimiento de la CPU se deterioran por grandes cantidades de datos. Lo perfilé con un archivo de 2MB en mi iPhone 5. La comparación de tiempo fue de 0.05 vs 12 segundos. La huella de memoria es despreciable con este método, mientras que el otro método aumentó el montón a 70 MB.

- (NSString *) hexString { NSUInteger bytesCount = self.length; if (bytesCount) { const char *hexChars = "0123456789ABCDEF"; const unsigned char *dataBuffer = self.bytes; char *chars = malloc(sizeof(char) * (bytesCount * 2 + 1)); if (chars == NULL) { // malloc returns null if attempting to allocate more memory than the system can provide. Thanks Cœur [NSException raise:@"NSInternalInconsistencyException" format:@"Failed to allocate more memory" arguments:nil]; return nil; } char *s = chars; for (unsigned i = 0; i < bytesCount; ++i) { *s++ = hexChars[((*dataBuffer & 0xF0) >> 4)]; *s++ = hexChars[(*dataBuffer & 0x0F)]; dataBuffer++; } *s = ''/0''; NSString *hexString = [NSString stringWithUTF8String:chars]; free(chars); return hexString; } return @""; }


Aquí hay una forma más rápida de hacer la conversión:

BenchMark (tiempo medio para una conversión de datos de 1024 bytes repetida 100 veces):

Dave Gallagher: ~ 8.070 ms
NSProgrammer: ~ 0.077 ms
Peter: ~ 0.031 ms
Este: ~ 0.017 ms

@implementation NSData (BytesExtras) static char _NSData_BytesConversionString_[512] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; -(NSString*)bytesString { UInt16* mapping = (UInt16*)_NSData_BytesConversionString_; register UInt16 len = self.length; char* hexChars = (char*)malloc( sizeof(char) * (len*2) ); // --- Coeur''s contribution - a safe way to check the allocation if (hexChars == NULL) { // we directly raise an exception instead of using NSAssert to make sure assertion is not disabled as this is irrecoverable [NSException raise:@"NSInternalInconsistencyException" format:@"failed malloc" arguments:nil]; return nil; } // --- register UInt16* dst = ((UInt16*)hexChars) + len-1; register unsigned char* src = (unsigned char*)self.bytes + len-1; while (len--) *dst-- = mapping[*src--]; NSString* retVal = [[NSString alloc] initWithBytesNoCopy:hexChars length:self.length*2 encoding:NSASCIIStringEncoding freeWhenDone:YES]; #if (!__has_feature(objc_arc)) return [retVal autorelease]; #else return retVal; #endif } @end


Aquí hay una solución usando Swift 3

extension Data { public var hexadecimalString : String { var str = "" enumerateBytes { buffer, index, stop in for byte in buffer { str.append(String(format:"%02x",byte)) } } return str } } extension NSData { public var hexadecimalString : String { return (self as Data).hexadecimalString } }


Cambie %08x a %08X para obtener caracteres principales.


El uso de la propiedad de descripción de NSData no debe considerarse un mecanismo aceptable para la codificación HEX de la cadena. Esa propiedad es solo para descripción y puede cambiar en cualquier momento. Como nota, antes de iOS, la propiedad de descripción NSData ni siquiera devolvió sus datos en forma hexadecimal.

Perdón por insistir en la solución, pero es importante aprovechar la energía para serializarla sin tener que recurrir a una API destinada a otra cosa que no sea la serialización de datos.

@implementation NSData (Hex) - (NSString*)hexString { NSUInteger length = self.length; unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (length*2)); unsigned char* bytes = (unsigned char*)self.bytes; for (NSUInteger i = 0; i < length; i++) { unichar c = bytes[i] / 16; if (c < 10) { c += ''0''; } else { c += ''A'' - 10; } hexChars[i*2] = c; c = bytes[i] % 16; if (c < 10) { c += ''0''; } else { c += ''A'' - 10; } hexChars[i*2+1] = c; } NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars length:length*2 freeWhenDone:YES]; return [retVal autorelease]; } @end


Esta es una categoría aplicada a NSData que escribí. Devuelve un NSString hexadecimal que representa NSData, donde los datos pueden tener cualquier longitud. Devuelve una cadena vacía si NSData está vacío.

NSData + Conversion.h

#import <Foundation/Foundation.h> @interface NSData (NSData_Conversion) #pragma mark - String Conversion - (NSString *)hexadecimalString; @end

NSData + Conversion.m

#import "NSData+Conversion.h" @implementation NSData (NSData_Conversion) #pragma mark - String Conversion - (NSString *)hexadecimalString { /* Returns hexadecimal string of NSData. Empty string if data is empty. */ const unsigned char *dataBuffer = (const unsigned char *)[self bytes]; if (!dataBuffer) return [NSString string]; NSUInteger dataLength = [self length]; NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; for (int i = 0; i < dataLength; ++i) [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; return [NSString stringWithString:hexString]; } @end

Uso:

NSData *someData = ...; NSString *someDataHexadecimalString = [someData hexadecimalString];

Esto es "probablemente" mejor que llamar [someData description] y luego [someData description] los espacios, <''s y>'' s. Desnudar a los personajes simplemente se siente demasiado "hacky". Además, nunca se sabe si Apple cambiará el formato de la descripción de -description en el futuro.

NOTA: He tenido personas que se comunicaron conmigo sobre licencias para el código en esta respuesta. Por la presente, dedico mi derecho de autor sobre el código que publiqué en esta respuesta al dominio público.


La respuesta de Peter trasladada a Swift

func hexString(data:NSData)->String{ if data.length > 0 { let hexChars = Array("0123456789abcdef".utf8) as [UInt8]; let buf = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length); var output = [UInt8](count: data.length*2 + 1, repeatedValue: 0); var ix:Int = 0; for b in buf { let hi = Int((b & 0xf0) >> 4); let low = Int(b & 0x0f); output[ix++] = hexChars[ hi]; output[ix++] = hexChars[low]; } let result = String.fromCString(UnsafePointer(output))!; return result; } return ""; }

swift3

func hexString()->String{ if count > 0 { let hexChars = Array("0123456789abcdef".utf8) as [UInt8]; return withUnsafeBytes({ (bytes:UnsafePointer<UInt8>) -> String in let buf = UnsafeBufferPointer<UInt8>(start: bytes, count: self.count); var output = [UInt8](repeating: 0, count: self.count*2 + 1); var ix:Int = 0; for b in buf { let hi = Int((b & 0xf0) >> 4); let low = Int(b & 0x0f); output[ix] = hexChars[ hi]; ix += 1; output[ix] = hexChars[low]; ix += 1; } return String(cString: UnsafePointer(output)); }) } return ""; }


Necesitaba resolver este problema y encontré las respuestas aquí muy útiles, pero me preocupa el rendimiento. La mayoría de estas respuestas implican copiar los datos a granel de NSData, así que escribí lo siguiente para realizar la conversión con poca sobrecarga:

@interface NSData (HexString) @end @implementation NSData (HexString) - (NSString *)hexString { NSMutableString *string = [NSMutableString stringWithCapacity:self.length * 3]; [self enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop){ for (NSUInteger offset = 0; offset < byteRange.length; ++offset) { uint8_t byte = ((const uint8_t *)bytes)[offset]; if (string.length == 0) [string appendFormat:@"%02X", byte]; else [string appendFormat:@" %02X", byte]; } }]; return string; }

Esto preasigna espacio en la cadena para el resultado completo y evita que se copie el contenido NSData utilizando enumerateByteRangesUsingBlock. Cambiar la X a una x en la cadena de formato utilizará dígitos hexadecimales en minúscula. Si no desea un separador entre los bytes, puede reducir la declaración

if (string.length == 0) [string appendFormat:@"%02X", byte]; else [string appendFormat:@" %02X", byte];

hasta justo

[string appendFormat:@"%02X", byte];


Necesitaba una respuesta que funcionara para cadenas de longitud variable, así que esto es lo que hice:

+ (NSString *)stringWithHexFromData:(NSData *)data { NSString *result = [[data description] stringByReplacingOccurrencesOfString:@" " withString:@""]; result = [result substringWithRange:NSMakeRange(1, [result length] - 2)]; return result; }

Funciona muy bien como una extensión para la clase NSString.


Siempre puede usar [yourString uppercaseString] para poner en mayúscula las letras en la descripción de los datos


Una mejor forma de serializar / deserializar NSData en NSString es usar el codificador / decodificador de Google Toolbox para Mac Base64. Simplemente arrastre hacia su aplicación Proyecte los archivos GTMBase64.m, GTMBase64.he GTMDefines.h del paquete Foundation y haga algo como

/** * Serialize NSData to Base64 encoded NSString */ -(void) serialize:(NSData*)data { self.encodedData = [GTMBase64 stringByEncodingData:data]; } /** * Deserialize Base64 NSString to NSData */ -(NSData*) deserialize { return [GTMBase64 decodeString:self.encodedData]; }


Swift + Propiedad.

Prefiero tener la representación hexadecimal como propiedad (lo mismo que los bytes y las propiedades de description ):

extension NSData { var hexString: String { let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes), count: self.length) return buffer.map { String(format: "%02x", $0) }.joinWithSeparator("") } var heXString: String { let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes), count: self.length) return buffer.map { String(format: "%02X", $0) }.joinWithSeparator("") } }

Idea es tomada de esta answer


@implementation NSData (Extn) - (NSString *)description { NSMutableString *str = [[NSMutableString alloc] init]; const char *bytes = self.bytes; for (int i = 0; i < [self length]; i++) { [str appendFormat:@"%02hhX ", bytes[i]]; } return [str autorelease]; } @end Now you can call NSLog(@"hex value: %@", data)


[deviceToken description]

Deberás eliminar los espacios.

Personalmente, yo base64 codifica el deviceToken , pero es una cuestión de gusto.