将NSData序列化为十六进制字符串的最佳方法

sar*_*ata 99 iphone notifications nsdata

我正在寻找一种很好的可可方式将NSData对象序列化为十六进制字符串.我们的想法是将用于通知的deviceToken序列化,然后再将其发送到我的服务器.

我有以下实现,但我认为必须有一些更短更好的方法来实现它.

+ (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;
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*her 201

这是我写的一个应用于NSData的类别.它返回表示NSData的十六进制NSString,其中数据可以是任意长度.如果NSData为空,则返回空字符串.

的NSData + Conversion.h

#import <Foundation/Foundation.h>

@interface NSData (NSData_Conversion)

#pragma mark - String Conversion
- (NSString *)hexadecimalString;

@end
Run Code Online (Sandbox Code Playgroud)

的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
Run Code Online (Sandbox Code Playgroud)

用法:

NSData *someData = ...;
NSString *someDataHexadecimalString = [someData hexadecimalString];
Run Code Online (Sandbox Code Playgroud)

这可能比调用[someData description]然后剥离空格,<'s和> 更好".剥离字符只是觉得太"hacky".另外,你永远不知道Apple将来会改变NSData的格式-description.

注意:我已经让人们联系我这个答案的代码许可.我特此将我在此公共领域的答案中发布的代码的版权归为公共领域.

  • 我不得不删除(unsigned long)强制转换并使用@"%02hhx"作为格式字符串来使其工作. (5认同)
  • 为什么这没有更多的投票?!?这是执行此任务的一种非常干净的方式.+1. (4认同)
  • 很好,但有两个建议:(1)我认为appendFormat对于大数据更有效,因为它避免了创建中间NSString和(2)%x表示unsigned int而不是unsigned long,尽管差别是无害的. (4认同)

Pet*_*ter 30

这是一个高度优化的NSData类别方法,用于生成十六进制字符串.虽然@Dave Gallagher的答案对于相对较小的尺寸来说足够了,但是对于大量数据,内存和CPU性能会下降.我在iPhone 5上用2MB文件对此进行了分析.时间比较为0.05对12秒.使用此方法可以忽略内存占用,而另一种方法将堆增加到70MB!

- (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 @"";
}
Run Code Online (Sandbox Code Playgroud)

  • @Moose,请更准确地参考您正在谈论的答案:投票和新答案可能会影响答案的定位。[编辑:哦,让我猜猜,你的意思是你自己的答案......] (2认同)

NSP*_*mer 17

使用NSData的description属性不应被视为HEX编码字符串的可接受机制.该属性仅供说明,可随时更改.作为一个注释,在iOS之前,NSData描述属性甚至没有以十六进制形式返回它的数据.

很抱歉在解决方案上喋喋不休,但重要的是要花费精力对其进行序列化,而不需要支持除了数据序列化之外的其他方面的API.

@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
Run Code Online (Sandbox Code Playgroud)

  • @karim,这是不正确的.通过使用initWithCharactersNoCopy:length:freeWhenDone:并且让freeWhenDone为YES,NSString将控制该字节缓冲区.调用free(hexChars)将导致崩溃.这里的好处很大,因为NSString不必进行昂贵的memcpy调用. (3认同)

Moo*_*ose 8

这是一种更快速的转换方式:

BenchMark(重复100次1024字节数据转换的平均时间):

Dave Gallagher:~8.070 ms
NSProgrammer:~0.077 ms
Peter:~0.031 ms
这一个:~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
Run Code Online (Sandbox Code Playgroud)


Niñ*_*ipt 8

功能Swift版本

一个班轮:

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

这是一个可重用且自我记录的扩展形式:

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("")
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,使用reduce("", combine: +)而不是joinWithSeparator("")被同行视为功能主人.


编辑:我将字符串($ 0,基数:16)更改为字符串(格式:"%02x",$ 0),因为填充零所需的一位数字


joh*_*n07 7

彼得的回答移植到斯威夫特

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 "";
}
Run Code Online (Sandbox Code Playgroud)

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 "";
}
Run Code Online (Sandbox Code Playgroud)