NSData को हेक्सडेक्सिमल स्ट्रिंग में क्रमबद्ध करने का सबसे अच्छा तरीका है


101

मैं एक 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;
}

जवाबों:


206

यह NSData पर लागू एक श्रेणी है जिसे मैंने लिखा था। यह NSData का प्रतिनिधित्व करते हुए एक हेक्साडेसिमल NSString देता है, जहां डेटा किसी भी लम्बाई का हो सकता है। यदि NSData खाली है तो एक खाली स्ट्रिंग लौटाता है।

NSData + Conversion.h

#import <Foundation/Foundation.h>

@interface NSData (NSData_Conversion)

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

@end

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

उपयोग:

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

यह "संभवतः" कॉलिंग से बेहतर है [someData description]और फिर रिक्त स्थान को अलग करना, <'s, और>' s है। स्ट्रिपिंग कैरेक्टर सिर्फ "हैकी" लगता है। इसके अलावा आप कभी नहीं जानते कि क्या Apple -descriptionभविष्य में NSData के स्वरूपण को बदल देगा ।

नोट: मेरे पास इस उत्तर में कोड के लिए लाइसेंस देने के बारे में लोग मेरे पास पहुंच गए हैं। मैं इस कोड को सार्वजनिक डोमेन में इस उत्तर में पोस्ट करके अपना कॉपीराइट समर्पित करता हूं।


4
अच्छा है, लेकिन दो सुझाव हैं: (1) मुझे लगता है कि बड़े डेटा के लिए appendFormat अधिक कुशल है क्योंकि यह एक मध्यवर्ती NSString बनाने से बचता है और (2)% x अहस्ताक्षरित लंबे के बजाय एक अहस्ताक्षरित इंट का प्रतिनिधित्व करता है, हालांकि यह अंतर हानिरहित है।
svachalek

एक naysayer नहीं है, क्योंकि यह एक अच्छा समाधान है जिसका उपयोग करना आसान है लेकिन 25 जनवरी को मेरा समाधान कहीं अधिक कुशल है। यदि आप प्रदर्शन अनुकूलित उत्तर की तलाश में हैं, तो उस उत्तर को देखें । समाधान को समझने के लिए इस उत्तर को एक अच्छा, आसान समझना।
NSProgrammer

5
मुझे इस कार्य को करने के लिए प्रारूपित स्ट्रिंग के रूप में (अहस्ताक्षरित लंबी) कास्ट को निकालना और @ "% 02hx" का उपयोग करना था।
एंटोन

1
सही, प्रति डेवलपर . "%02lx"(unsigned int)@"%02hhx"
apple.com/library/ios/documentation/cocoa/conceptual/…

1
[hexString appendFormat:@"%02x", (unsigned int)dataBuffer[i]];बहुत बेहतर है (छोटी मेमोरी पदचिह्न)
मारेक आर

31

यहाँ हेक्स स्ट्रिंग उत्पन्न करने के लिए एक उच्च अनुकूलित NSData श्रेणी विधि है। जबकि @Dave Gallagher का जवाब अपेक्षाकृत छोटे आकार के लिए पर्याप्त है, मेमोरी और सीपीयू प्रदर्शन बड़ी मात्रा में डेटा के लिए बिगड़ते हैं। मैंने अपने iPhone 5 पर 2MB फ़ाइल के साथ इसे प्रोफाइल किया। समय की तुलना 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 @"";
}

नाइस वन @ पेटर - हालाँकि, इससे भी तेज (आपके) समाधान से बहुत अधिक नहीं है - बस नीचे;)
मूस

1
@ नमस्कार, कृपया अधिक सटीक संदर्भ दें, जिसके बारे में आप बात कर रहे हैं: वोट और नए उत्तर उत्तरों की स्थिति को प्रभावित कर सकते हैं। [संपादित करें: ओह, मुझे लगता है, आप अपने खुद के जवाब का मतलब है ...]
C'ur

1
जोड़ा गया मॉलोक नल चेक। धन्यवाद @ Cur
पीटर

17

NSData की विवरण संपत्ति का उपयोग करके एचईएक्स को स्ट्रिंग एन्कोडिंग के लिए एक स्वीकार्य तंत्र नहीं माना जाना चाहिए। वह संपत्ति केवल विवरण के लिए है और किसी भी समय बदल सकती है। एक नोट के रूप में, पूर्व-आईओएस, NSData विवरण संपत्ति भी हेक्स रूप में डेटा नहीं है।

समाधान पर वीणा के लिए खेद है, लेकिन यह महत्वपूर्ण है कि यह बिना किसी गुल्लक के पीजी-बैकिंग के बिना इसे प्रसारित करने के लिए ऊर्जा का उपयोग करें जो कि डेटा क्रमांकन के अलावा किसी और चीज़ के लिए है।

@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

हालाँकि, आपको वापसी से पहले (हेक्सचर्स) मुक्त करना होगा।
करीम

3
@ करीम, यह गलत है। InitWithCharactersNoCopy का उपयोग करके: लंबाई: freeWhenDone: और freeWhenDone YES होने के नाते, NSString उस बाइट बफर का नियंत्रण लेगा। मुफ्त (hexChars) कॉल करने के परिणामस्वरूप दुर्घटना होगी। यहाँ लाभ काफी है क्योंकि NSString को कोई महंगा मेम्ची कॉल नहीं करना पड़ेगा।
NSProgrammer

@NSProgrammer धन्यवाद मैंने एनएसएसटिंग इनिशियलाइज़र को नोटिस नहीं किया।
करीम

प्रलेखन में कहा गया है कि descriptionहेक्स एन्कोडेड स्ट्रिंग लौटाता है, इसलिए यह मुझे उचित लगता है।
असामान्य

क्या हमें संभावित रूप से अशक्त होने के लिए मॉलोक लौटाए गए मूल्य की जांच नहीं करनी चाहिए?
कूर 5

9

यहाँ रूपांतरण करने का एक तेज़ तरीका है:

बेंचमार्क (1024 बाइट डेटा रूपांतरण के लिए समय 100 बार दोहराया गया):

डेव गैलाघर: ~ 8.070 एमएस
एनएसप्रोग्रामर: ~ 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

1
आप देख सकते हैं कि मैंने कैसे मालॉक चेक यहाँ लागू किया ( _hexStringविधि): github.com/ZipArchive/ZipArchive/blob/master/SSZipArchive/…
Cœur

संदर्भ के लिए धन्यवाद - BTW मुझे 'बहुत लंबा' पसंद है - यह सच है, लेकिन अब मैंने इसे टाइप किया है, कोई भी कॉपी / पेस्ट कर सकता है - मैं मजाक कर रहा हूं - मैंने इसे उत्पन्न किया है - आप पहले से ही जानते थे :) आप सही हैं लंबा, मैं बस हर जगह हिट करने की कोशिश कर रहा था जो मैं माइक्रोसेकंड जीत सकता हूं! यह लूप पुनरावृत्तियों को 2 से विभाजित करता है। लेकिन मैं मानता हूं कि इसमें लालित्य की कमी है। अलविदा
मूस

8

कार्यात्मक स्विफ्ट संस्करण

एक लाइन:

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, मूलांक: 16) को स्ट्रिंग में बदल दिया (प्रारूप: "% 02x", $ 0), क्योंकि एक अंक संख्या में एक गद्दी शून्य होने की आवश्यकता होती है


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

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

स्विफ्ट 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 "";
}

4

मुझे इस समस्या को हल करने की आवश्यकता थी और यहां उत्तर बहुत उपयोगी मिले, लेकिन मुझे प्रदर्शन की चिंता है। इनमें से अधिकांश उत्तरों में 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;
}

यह पूरे परिणाम के लिए स्ट्रिंग में पूर्व-आवंटित करता है और enumerateByteRangesUsingBlock का उपयोग करके कभी भी NSData सामग्री को कॉपी करने से बचा जाता है। प्रारूप स्ट्रिंग में एक्स को एक्स में बदलने से लोअरकेस हेक्स अंकों का उपयोग होगा। यदि आप बाइट्स के बीच एक विभाजक नहीं चाहते हैं तो आप बयान को कम कर सकते हैं

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

नीचे बस

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

2
मेरा मानना ​​है कि सूचकांक बाइट मान की आवश्यकता को पुनः प्राप्त करने के लिए समायोजन करता है, क्योंकि NSRangeबड़े NSDataबाइट्स बफर के भीतर सीमा को इंगित करता है, न कि छोटे बाइट्स बफ़र के भीतर (जो आपूर्ति किए गए ब्लॉक का पहला पैरामीटर है enumerateByteRangesUsingBlock) जो कि बड़े के एकल सन्निहित हिस्से का प्रतिनिधित्व करता है NSData। इस प्रकार, byteRange.lengthबाइट्स बफर के आकार को दर्शाता है, लेकिन byteRange.locationबड़े के भीतर स्थान है NSData। इस प्रकार, आप बाइट को पुनः प्राप्त करने के लिए बस offsetनहीं byteRange.location + offset, का उपयोग करना चाहते हैं ।
रोब

1
@ रब धन्यवाद, मैं देख रहा हूं कि आपका क्या मतलब है और कोड को समायोजित किया है
जॉन स्टीफन

1
आप बयान नीचे संशोधित करते हैं सिर्फ एक का उपयोग करने के appendFormatआप शायद भी बदलना चाहिए self.length * 3करने के लिएself.length * 2
टी Colligan

1

मुझे एक उत्तर की आवश्यकता थी जो चर लंबाई के तार के लिए काम करेगा, इसलिए यहाँ मैंने क्या किया:

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

NSString वर्ग के लिए एक विस्तार के रूप में महान काम करता है।


1
क्या होगा यदि Apple वर्णन का प्रतिनिधित्व करने के तरीके को बदलता है?
ब्रेंडेन

1
iOS13 विवरण विधि में एक अलग प्रारूप है।
nacho4d


1

NSData को NSString में अनुक्रमित / डिस्क्रिअलाइज़ करने का एक बेहतर तरीका मैक बेस 64 एनकोडर / डिकोडर के लिए Google टूलबॉक्स का उपयोग करना है । बस अपने ऐप में खींचें प्रोजेक्ट्स 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];

}

स्रोत कोड को देखकर ऐसा लगता है कि यह प्रदान करने वाला वर्ग अब GTMStringEncoding है। मैंने इसे करने की कोशिश नहीं की है लेकिन यह इस सवाल का एक शानदार नया समाधान है।
सरफता

1
Mac OS X 10.6 / iOS 4.0 के रूप में, NSData बेस -64 एनकोडिंग करता है। string = [data base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]
20

@jrc जो सच है, लेकिन बेस -64 में वास्तविक कार्य तार को एन्कोड करने पर विचार करें। आपको "वेब सुरक्षित" एन्कोडिंग की तुलना में निपटना होगा, कि आपके पास iOS / MacOS में नहीं है, जैसे GTMBase64 # webSafeEncodeData में। इसके अलावा, आपको Base64 "गद्दी" जोड़ने / हटाने की आवश्यकता हो सकती है, इसलिए आपके पास यह विकल्प भी है: GTMBase64 # stringByWebSafeEncodingData: (NSData *) डेटा गद्देदार: (BOOL) गद्देदार;
लोरेटोपरसी

1

यहाँ स्विफ्ट 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
    }

}

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

0

पूंजी वर्ण प्राप्त %08xकरने के %08Xलिए बदलें ।


6
यह एक टिप्पणी के रूप में बेहतर होगा क्योंकि आप किसी भी संदर्भ को शामिल नहीं करते हैं। बस '
ब्रेंडेन

0

स्विफ्ट + संपत्ति।

मैं संपत्ति के रूप में हेक्स प्रतिनिधित्व करना पसंद करता हूं (समान 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("")
    }
}

इस उत्तर से आइडिया उधार लिया जाता है


-4
[deviceToken description]

आपको रिक्त स्थान निकालने की आवश्यकता होगी।

व्यक्तिगत रूप से मैं base64सांकेतिक शब्दों में बदलना deviceToken, लेकिन यह स्वाद की बात है।


इसका एक ही परिणाम नहीं मिलता है। विवरण रिटर्न: <2cf56d5d 2fab0a47 ... 7738ce77 7e791759> जबकि मैं देख रहा हूँ: 2CF56D5D2FAB0A47 .... 7738CE777E792759
सरफटा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.