سؤال

أنا أبحث عن طريقة لطيفة الكاكاو لتسليط كائن NSDATA في سلسلة ست عشرية. الفكرة هي تسلسل البطولة المستخدمة للإشعار قبل إرسالها إلى الخادم الخاص بي.

لدي التنفيذ التالي، لكنني أفكر في أنه يجب أن يكون هناك بعض طريقة أقصر وأجذب للقيام بذلك.

+ (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;
}
هل كانت مفيدة؟

المحلول

هذه فئة مطبقة على NSDATA كتبت. تقوم بإرجاع NSString عشري يمثل NSDATA، حيث يمكن أن تكون البيانات أي طول. إرجاع سلسلة فارغة إذا كانت NSDATA فارغة.

nsdata + تحويل.h.

#import <Foundation/Foundation.h>

@interface NSData (NSData_Conversion)

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

@end

nsdata + تحويل.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

الاستعمال:

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

هذا هو "ربما" أفضل من الاتصال [someData description] ثم تجريد المساحات، <s، و>. تجريد الشخصيات يشعر فقط "Hacky". بالإضافة إلى أنك لا تعرف أبدا ما إذا كانت Apple ستغير تنسيق NSDATA -description فى المستقبل.

ملاحظة: لقد وصلت إلي أشخاصا إليك حول الترخيص للحصول على التعليمات البرمجية في هذه الإجابة. أهلا بموجبه حقوق الطبع والنشر في التعليمات البرمجية التي نشرتها في هذه الإجابة على المجال العام.

نصائح أخرى

إليك الأمثل للغاية NSDATA الفئة طريقة لتوليد سلسلة عرافة. في حين أن الإجابة Dave Gallagher كافية لحجم صغير نسبيا، فإن أداء الذاكرة و CPU يتدهور كميات كبيرة من البيانات. قمت بمرض هذا مع ملف 2MB على iPhone الخاص بي 5. كانت مقارنة الوقت 0.05 مقابل 12 ثانية. بصمة الذاكرة لا تذكر بهذه الطريقة بينما نمت الطريقة الأخرى كومة إلى 70 ميجابايت!

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

يجب عدم اعتبار استخدام خاصية الوصف لنظام NSDATA آلية مقبولة لرسء الزيادة. هذه الخاصية هي للوصف فقط ويمكن أن تتغير في أي وقت. كملاحظة، Pre-iOS، لم تعد خاصية وصف NSDATA حتى بيانات HEX.

اسف على العزف على الحل ولكن من المهم أن تأخذ الطاقة لتسلسلها دون دعم أصبع إيقاف 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

نسخة سريعة وظيفية

واحد بطانة:

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("") أن ينظر إليها على أنها سيد وظيفي من قبل أقرانك.


تحرير: لقد غيرت السلسلة (0 $ 0، radix: 16) إلى السلسلة (التنسيق:٪ 02x "، $ 0)، لأن أرقام رقم واحد اللازمة لحصي الحشو الصفر

إليك طريقة أسرع للقيام بالتحويل:

المعيار (يعني وقت تحويل بيانات 1024 بايت تكرار 100 مرة):

ديف غالاغر: ~ 8.070 MS
NSProgrammer: ~ 0.077 MS
بيتر: ~ 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

إجابة بيتر استند إلى SWIFT

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

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

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

كنت بحاجة إلى حل هذه المشكلة ووجدت الإجابات هنا مفيدة للغاية، لكنني قلق بشأن الأداء. معظم هذه الإجابات تنطوي على نسخ البيانات بكميات كبيرة من 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;
}

هذا يخصص مسبقا الفضاء في السلسلة للحصول على النتيجة بأكملها ويتجنب نسخ محتويات NSDATA على الإطلاق باستخدام InumerByterangesusingBlock. سيؤدي تغيير X إلى X في سلسلة التنسيق إلى استخدام أرقام Hex صغيرة. إذا كنت لا تريد فاصل بين البايتات، يمكنك تقليل البيان

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

إلى أسفل فقط

[string appendFormat:@"%02X", byte];

كنت بحاجة إلى إجابة من شأنها أن تعمل على سلاسل متغيرة الطول، لذلك إليك ما فعلته:

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

يعمل بشكل رائع كملحق لفئة NSSTRING.

يمكنك دائما استخدام [YourString UpperCasString] لاستفادة الحروف في وصف البيانات

طريقة أفضل لتسلسل / تحيز NSDATA إلى NSSTRING هو استخدام جوجل Toolbox لنظام التشغيل Mac Base64 التشفير / فك الترميز. فقط اسحب إلى تطبيق التطبيق الخاص بك ملفات GTMBASE64.M، GTMBASE64.HE GTMDefines.h من مؤسسة الحزمة والفعل شيء مثل

/**
 * 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];

}

هنا حل باستخدام 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
    }

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

يتغيرون %08x ل %08X للحصول على أحرف رأس المال.

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

يتم استعارة الفكرة من هذا إجابه

[deviceToken description]

ستحتاج إلى إزالة المساحات.

انا شخصيا base64 تشفير deviceToken, ، لكنها مسألة ذوق.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top