代码之家  ›  专栏  ›  技术社区  ›  sarfata

将NSData序列化为hexadeximal字符串的最佳方法

  •  103
  • sarfata  · 技术社区  · 17 年前

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

    + (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;
    }
    
    15 回复  |  直到 17 年前
        1
  •  208
  •   Dave    11 年前

    NSData+转换。h

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

    NSData+转换。米

    #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
    

    用途:

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

    这“可能”比打电话好 [someData description] 然后剥离空间<和>s剥离角色感觉太“黑客”了。另外,你永远不知道苹果是否会改变NSData的格式 -description 未来。

    我让人们联系我,询问这个答案中代码的许可问题。我特此将我在本答案中发布的代码的版权归公共领域所有。

        2
  •  31
  •   junjie Peter    6 年前

    这是一个高度优化的 NSData category 生成十六进制字符串的方法。虽然@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 @"";
    }
    
        3
  •  17
  •   NSProgrammer    10 年前

    很抱歉重复了这个解决方案,但重要的是要花精力对其进行序列化,而不要背负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
    
        4
  •  10
  •   Moose    8 年前

    以下是一种更快的转换方法:

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

    戴夫·加拉格尔:约8.070毫秒
    N编程器:~0.077毫秒
    彼得:~0.031毫秒
    这个:~0.017毫秒

    @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
    
        5
  •  8
  •   NiñoScript    10 年前

    Swift功能版本

    一个衬垫:

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

    以下是一个可重用且自文档化的扩展表单:

    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("")
        }
    }
    

    reduce("", combine: +) 而不是 joinWithSeparator("")


    编辑:我将String($0,基数:16)更改为String(格式:“%02x”,$0),因为一位数需要填充零

        6
  •  7
  •   john07    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 "";
    }
    

    快速3

    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 "";
    }
    

    Swift 5

    func hexString()->String{
        if count > 0 {
            let hexChars = Array("0123456789abcdef".utf8) as [UInt8];
            return withUnsafeBytes{ bytes->String in
                var output = [UInt8](repeating: 0, count: bytes.count*2 + 1);
                var ix:Int = 0;
                for b in bytes {
                    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 "";
    }
    
        7
  •  4
  •   John Stephen    12 年前

    我需要解决这个问题,发现这里的答案非常有用,但我担心性能。这些答案大多涉及从NSData中批量复制数据,因此我编写了以下代码以低开销进行转换:

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

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

    下降到只是

    [string appendFormat:@"%02X", byte];
    
        8
  •  1
  •   BadPirate    15 年前

    我需要一个适用于可变长度字符串的答案,所以我这样做了:

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

        9
  •  1
  •   Rostyslav Bachyk    15 年前

    您始终可以使用[yourString uppercaseString]在数据描述中将字母大写

        10
  •  1
  •   loretoparisi    15 年前

    将NSData序列化/反序列化为NSString的更好方法是使用 Google Toolbox for Mac

    /**
     * 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];
    
    }
    
        11
  •  1
  •   Alex    10 年前

    这是一个使用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
        }
    
    }
    
        12
  •  0
  •   Ramesh    12 年前
    @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)
    
        13
  •  0
  •   newfurniturey    12 年前

    %08x %08X 获得大写字符。

        14
  •  0
  •   Community Mohan Dere    9 年前

    Swift+房地产。

    我更喜欢用十六进制表示作为属性(与 bytes 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("")
        }
    }
    

    这个想法是借来的 answer

        15
  •  -4
  •   Matthew Verstraete Rjev    9 年前
    [deviceToken description]
    

    您需要删除空格。

    就我个人而言 base64 编码 deviceToken ,但这是品味的问题。