Как преобразовать NSData в строку NSString Hex?
Когда я вызываю -description
на объект NSData
, я вижу симпатичную шестнадцатеричную строку байтов объекта NSData
, например:
<f6e7cd28 0fc5b5d4 88f8394b af216506 bc1bba86 4d5b483d>
Я хотел бы получить это представление данных (минус lt/gt кавычки) во встроенную память NSString
, чтобы я мог работать с ней. Я бы предпочел не называть -[NSData description]
, а затем просто обрезайте lt/gt кавычки (потому что я предполагаю, что это не гарантированный аспект публичного интерфейса NSData
и в будущем будет изменен).
Какой самый простой способ получить это представление объекта NSData
в объект NSString
(кроме вызова -description
)?
Ответы
Ответ 1
Имейте в виду, что любое решение String(format: ...)
будет ужасно медленным (для больших данных)
NSData *data = ...;
NSUInteger capacity = data.length * 2;
NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity];
const unsigned char *buf = data.bytes;
NSInteger i;
for (i=0; i<data.length; ++i) {
[sbuf appendFormat:@"%02X", (NSUInteger)buf[i]];
}
Если вам нужно что-то более результативно, попробуйте это:
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<len; i++) {
buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
buf[i*2+1] = itoh(bytes[i] & 0xF);
}
return [[NSString alloc] initWithBytesNoCopy:buf
length:len*2
encoding:NSASCIIStringEncoding
freeWhenDone:YES];
}
версия Swift 3
extension NSData {
var hexString: String? {
let buf = bytes.assumingMemoryBound(to: UInt8.self)
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 = length * 2
let ptr = UnsafeMutablePointer<UInt8>.allocate(capacity: hexLen)
for i in 0 ..< length {
ptr[i*2] = itoh((buf[i] >> 4) & 0xF)
ptr[i*2+1] = itoh(buf[i] & 0xF)
}
return String(bytesNoCopy: ptr, length: hexLen, encoding: .utf8, freeWhenDone: true)
}
}
Ответ 2
Я согласен с решением не вызывать description
, который должен быть зарезервирован для отладки, поэтому хороший вопрос и хороший вопрос:)
Простейшим решением является цикл через байты NSData
и построение NSString. Используйте [yourData bytes]
для доступа к байтам и создайте строку в NSMutableString
.
Вот пример, реализуя это, используя категорию NSDatap >
@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<nbBytes; ) {
[hex appendFormat:@"%02X", bytes[i]];
//We need to increment here so that the every-n-bytes computations are right.
++i;
if (spaces) {
if (i % lineBreakEveryThisManyBytes == 0) [hex appendString:@"\n"];
else if (i % spaceEveryThisManyBytes == 0) [hex appendString:@" "];
}
}
return [hex autorelease];
}
@end
Использование:
NSData* data = ...
NSString* hex = [data hexRepresentationWithSpaces_AS:YES];
Ответ 3
Мне понравился @Erik_Aigner ответ на все лучшее. Я просто немного переработал его:
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]];
}
Ответ 4
Просто хотел добавить, что метод @PassKits может быть написан очень элегантно с помощью Swift 3, поскольку Data
теперь представляет собой коллекцию.
extension Data {
var hex: String {
var hexString = ""
for byte in self {
hexString += String(format: "%02X", byte)
}
return hexString
}
}
Или...
extension Data {
var hex: String {
return self.map { b in String(format: "%02X", b) }.joined()
}
}
Или даже...
extension Data {
var hex: String {
return self.reduce("") { string, byte in
string + String(format: "%02X", byte)
}
}
}
Ответ 5
В Swift вы можете создать расширение.
extension NSData {
func toHexString() -> String {
var hexString: String = ""
let dataBytes = UnsafePointer<CUnsignedChar>(self.bytes)
for (var i: Int=0; i<self.length; ++i) {
hexString += String(format: "%02X", dataBytes[i])
}
return hexString
}
}
Затем вы можете просто использовать:
let keyData: NSData = NSData(bytes: [0x00, 0xFF], length: 2)
let hexString = keyData.toHexString()
println("\(hexString)") // Outputs 00FF
Ответ 6
К сожалению, нет встроенного способа создания hex из NSData, но это довольно легко сделать самому. Простым способом является просто передавать последовательные байты в sprintf ( "% 02x" ) и накапливать их в NSMutableString. Более быстрый способ заключается в создании таблицы поиска, которая отображает 4 бита в шестнадцатеричный символ, а затем передает последовательные nybbles в эту таблицу.
Ответ 7
Хотя это может быть не самый эффективный способ сделать это, если вы делаете это для отладки, SSCrypto имеет категорию в NSData, которая содержит два метода для этого (один для создания NSString исходных байтовых значений, и тот, который показывает более красивое его представление).
http://www.septicus.com/SSCrypto/trunk/SSCrypto.m
Ответ 8
Увидев, что в комментариях есть фрагмент Swift 1.2, здесь версия Swift 2, так как стиль C для циклов теперь устарел.
Gist с лицензией MIT и двумя простыми модульными тестами, если вам интересно.
Здесь код для вашего удобства:
import Foundation
extension NSData {
var hexString: String {
let pointer = UnsafePointer<UInt8>(bytes)
let array = getByteArray(pointer)
return array.reduce("") { (result, byte) -> String in
result.stringByAppendingString(String(format: "%02x", byte))
}
}
private func getByteArray(pointer: UnsafePointer<UInt8>) -> [UInt8] {
let buffer = UnsafeBufferPointer<UInt8>(start: pointer, count: length)
return [UInt8](buffer)
}
}