Подтвердить что ты не робот

Как преобразовать NSData в строку NSString Hex?

Когда я вызываю -description на объект NSData, я вижу симпатичную шестнадцатеричную строку байтов объекта NSData, например:

<f6e7cd28 0fc5b5d4 88f8394b af216506 bc1bba86 4d5b483d>

Я хотел бы получить это представление данных (минус lt/gt кавычки) во встроенную память NSString, чтобы я мог работать с ней. Я бы предпочел не называть -[NSData description], а затем просто обрезайте lt/gt кавычки (потому что я предполагаю, что это не гарантированный аспект публичного интерфейса NSData и в будущем будет изменен).

Какой самый простой способ получить это представление объекта NSData в объект NSString (кроме вызова -description)?

4b9b3361

Ответ 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.

Вот пример, реализуя это, используя категорию NSData​​p >

@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)
  }
}