Como converter um NSData em uma string NSString Hex?

Quando eu chamo -description em um object NSData , vejo uma string Hex bonita dos bytes do object NSData como:

  

Eu gostaria de obter essa representação dos dados (menos as citações lt / gt) em um NSString em memory para que eu possa trabalhar com ele .. Eu prefiro não chamar -[NSData description] e, em seguida, apenas cortar o lt / gt aspas (porque eu suponho que não é um aspecto garantido da interface pública da NSData e está sujeita a alterações no futuro).

Qual é a maneira mais simples de obter essa representação de um object NSData em um object NSString (diferente de chamar -description )?

Tenha em mente que qualquer solução de String(format: ...) será terrivelmente lenta (para dados grandes)

 NSData *data = ...; NSUInteger capacity = data.length * 2; NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity]; const unsigned char *buf = data.bytes; NSInteger i; for (i=0; i 

Se você precisar de algo com mais desempenho, tente isto:

 static inline char itoh(int i) { if (i > 9) return 'A' + (i - 10); return '0' + i; } NSString * NSDataToHex(NSData *data) { NSUInteger i, len; unsigned char *buf, *bytes; len = data.length; bytes = (unsigned char*)data.bytes; buf = malloc(len*2); for (i=0; i> 4) & 0xF); buf[i*2+1] = itoh(bytes[i] & 0xF); } return [[NSString alloc] initWithBytesNoCopy:buf length:len*2 encoding:NSASCIIStringEncoding freeWhenDone:YES]; } 

Versão 4.2 do Swift

 extension Data { var hexString: String? { return withUnsafeBytes { (bytes: UnsafePointer) in let charA = UInt8(UnicodeScalar("a").value) let char0 = UInt8(UnicodeScalar("0").value) func itoh(_ value: UInt8) -> UInt8 { return (value > 9) ? (charA + value - 10) : (char0 + value) } let hexLen = count * 2 let ptr = UnsafeMutablePointer.allocate(capacity: hexLen) for i in 0 ..< count { ptr[i*2] = itoh((bytes[i] >> 4) & 0xF) ptr[i*2+1] = itoh(bytes[i] & 0xF) } return String(bytesNoCopy: ptr, length: hexLen, encoding: .utf8, freeWhenDone: true) } } } 

Eu concordo com a solução para não chamar a description que deve ser reservada para a debugging, ponto tão bom e boa pergunta 🙂

A solução mais fácil é NSData os bytes do NSData e construir o NSString a partir dele. Use [yourData bytes] para acessar os bytes e construa a string em um NSMutableString .

Aqui está um exemplo implementando isso usando uma categoria de NSData

 @interface NSData(Hex) -(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces; @end @implementation NSData(Hex) -(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces { const unsigned char* bytes = (const unsigned char*)[self bytes]; NSUInteger nbBytes = [self length]; //If spaces is true, insert a space every this many input bytes (twice this many output characters). static const NSUInteger spaceEveryThisManyBytes = 4UL; //If spaces is true, insert a line-break instead of a space every this many spaces. static const NSUInteger lineBreakEveryThisManySpaces = 4UL; const NSUInteger lineBreakEveryThisManyBytes = spaceEveryThisManyBytes * lineBreakEveryThisManySpaces; NSUInteger strLen = 2*nbBytes + (spaces ? nbBytes/spaceEveryThisManyBytes : 0); NSMutableString* hex = [[NSMutableString alloc] initWithCapacity:strLen]; for(NSUInteger i=0; i 

Uso:

 NSData* data = ... NSString* hex = [data hexRepresentationWithSpaces_AS:YES]; 

Só queria acrescentar que o método @ PassKits pode ser escrito de forma muito elegante usando o Swift 3, pois Data now é uma coleção.

 extension Data { var hex: String { var hexString = "" for byte in self { hexString += String(format: "%02X", byte) } return hexString } } 

Ou …

 extension Data { var hex: String { return self.map { b in String(format: "%02X", b) }.joined() } } 

Ou até mesmo …

 extension Data { var hex: String { return self.reduce("") { string, byte in string + String(format: "%02X", byte) } } } 

Eu gostei da resposta do @ Erik_Aigner o melhor. Eu apenas refatorei um pouco:

 NSData *data = [NSMutableData dataWithBytes:"acani" length:5]; NSUInteger dataLength = [data length]; NSMutableString *string = [NSMutableString stringWithCapacity:dataLength*2]; const unsigned char *dataBytes = [data bytes]; for (NSInteger idx = 0; idx < dataLength; ++idx) { [string appendFormat:@"%02x", dataBytes[idx]]; } 

No Swift, você pode criar uma extensão.

 extension NSData { func toHexString() -> String { var hexString: String = "" let dataBytes = UnsafePointer(self.bytes) for (var i: Int=0; i 

Então você pode simplesmente usar:

 let keyData: NSData = NSData(bytes: [0x00, 0xFF], length: 2) let hexString = keyData.toHexString() println("\(hexString)") // Outputs 00FF 

Infelizmente, não há uma maneira interna de produzir hexadecimal a partir de um NSData, mas é muito fácil fazer você mesmo. A maneira simples é apenas passar bytes sucessivos no sprintf (“% 02x”) e acumulá-los em um NSMutableString. Uma maneira mais rápida seria construir uma tabela de consulta que mapeasse 4 bits em um caractere hexadecimal e, em seguida, passasse sucessivos nybbles para essa tabela.

Embora possa não ser a maneira mais eficiente de fazer isso, se você estiver fazendo isso para debugging, o SSCrypto tem uma categoria no NSData que contém dois methods para fazer isso (um para criar uma NSString dos valores brutos de byte e um que mostra uma representação mais bonita do mesmo).

http://www.septicus.com/SSCrypto/trunk/SSCrypto.m

Vendo que há um trecho do Swift 1.2 nos comentários, aqui está a versão do Swift 2, já que os loops de estilo C estão obsoletos agora. Gist com licença MIT e dois testes unitários simples se você se importa.

Aqui está o código para sua conveniência:

 import Foundation extension NSData { var hexString: String { let pointer = UnsafePointer(bytes) let array = getByteArray(pointer) return array.reduce("") { (result, byte) -> String in result.stringByAppendingString(String(format: "%02x", byte)) } } private func getByteArray(pointer: UnsafePointer) -> [UInt8] { let buffer = UnsafeBufferPointer(start: pointer, count: length) return [UInt8](buffer) } }