it-swarm.com.de

Die beste Möglichkeit, ein NSData in eine hexadezimale Zeichenfolge zu serialisieren

Ich suche einen Nice-Cocoa-Weg, um ein NSData-Objekt in eine hexadezimale Zeichenfolge zu serialisieren. Die Idee ist, das für die Benachrichtigung verwendete gerätetoken vor dem Senden an meinen Server zu serialisieren.

Ich habe die folgende Implementierung, denke aber, dass es einen kürzeren und schöneren Weg geben muss.

+ (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;
}
96
sarfata

Dies ist eine Kategorie, die auf NSData angewendet wurde, die ich geschrieben habe. Es gibt einen hexadezimalen NSString zurück, der die NSData darstellt, wobei die Daten eine beliebige Länge haben können. Gibt eine leere Zeichenfolge zurück, wenn NSData leer ist.

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

Verwendungszweck:

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

Dies ist "wahrscheinlich" besser als das Aufrufen von [someData description] und das anschließende Entfernen der Leerzeichen, <und ". Das Strippen von Charakteren fühlt sich einfach zu "hackig" an. Außerdem wissen Sie nie, ob Apple die Formatierung von -description von NSData in der Zukunft ändert.

HINWEIS: Ich hatte Leute, die sich wegen der Lizenzierung des Codes in dieser Antwort an mich wenden. Ich widme hiermit mein Urheberrecht an dem Code, den ich in dieser Antwort veröffentlicht habe.

194
Dave Gallagher

Hier ist eine stark optimierte NSData category Methode zum Erzeugen eines Hex-Strings. Während die Antwort von @Dave Gallagher für eine relativ kleine Größe ausreicht, verschlechtern sich Speicher- und CPU-Leistung bei großen Datenmengen. Ich habe dies mit einer 2MB-Datei auf meinem iPhone 5 erstellt. Der Zeitvergleich betrug 0,05 gegenüber 12 Sekunden. Der Speicherbedarf ist bei dieser Methode vernachlässigbar, während die andere Methode den Heapspeicher auf 70 MB vergrößerte!

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

Die Verwendung der Eigenschaft description von NSData sollte nicht als akzeptabler Mechanismus für die HEX-Kodierung der Zeichenfolge betrachtet werden. Diese Eigenschaft dient nur der Beschreibung und kann sich jederzeit ändern. Als eine Anmerkung, vor iOS, gab die NSData-Beschreibungseigenschaft ihre Daten nicht einmal in Hex-Form zurück.

Tut mir leid, dass Sie mit der Lösung harfen müssen, aber es ist wichtig, dass Sie die Energie zur Serialisierung mitnehmen, ohne von einer API abzulenken, die für etwas anderes als die Datenserialisierung gedacht ist.

@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
15
NSProgrammer

Funktionelle Swift-Version

Einzeiler:

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

Hier ist eine wiederverwendbare und selbstdokumentierende Erweiterungsform:

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

Alternativ können Sie reduce("", combine: +) anstelle von joinWithSeparator("") verwenden, um von Ihren Kollegen als funktioneller Master betrachtet zu werden.


Bearbeiten: Ich habe String ($ 0, Radix: 16) in String (Format: "% 02x", $ 0) geändert, da einstellige Zahlen eine Auffüllnullung benötigen

8
NiñoScript

Hier ist eine schnellere Methode für die Konvertierung:

BenchMark (mittlere Zeit für eine 1024 Byte-Datenkonvertierung, die 100 Mal wiederholt wurde)

Dave Gallagher: ~ 8.070 ms
NSProgrammierer: ~ 0,077 ms
Peter: ~ 0,031 ms
Dieser: ~ 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
8
Moose

Peters Antwort wurde auf Swift portiert 

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

Ich musste dieses Problem lösen und fand die Antworten hier sehr nützlich, aber ich mache mir Sorgen um die Leistung. Die meisten dieser Antworten beinhalten das Kopieren der Daten in Massen aus NSData. Daher habe ich Folgendes geschrieben, um die Konvertierung mit geringem Aufwand durchzuführen:

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

Dadurch wird Speicherplatz in der Zeichenfolge für das gesamte Ergebnis vorab zugewiesen, und es wird vermieden, dass der NSData-Inhalt mithilfe von enumerateByteRangesUsingBlock herauskopiert wird. Wenn Sie das X in ein x in der Formatzeichenfolge ändern, werden Hexadezimalzeichen in Kleinbuchstaben verwendet. Wenn Sie kein Trennzeichen zwischen den Bytes wünschen, können Sie die Anweisung reduzieren

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

nur um

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

Ich brauchte eine Antwort, die sich für Saiten mit variabler Länge eignet. Daher habe ich Folgendes getan:

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

Funktioniert hervorragend als Erweiterung für die NSString-Klasse.

2
BadPirate

Sie können [IhrenString GroßbuchstabenString] immer verwenden, um Buchstaben in der Datenbeschreibung groß zu schreiben

1

Eine bessere Möglichkeit, NSData in NSString zu serialisieren/deserialisieren, ist die Verwendung der Google Toolbox für Mac Base64-Encoder/Decoder. Ziehen Sie einfach die Dateien GTMBase64.m, GTMBase64.h und GTMDefines.h aus dem Paket Foundation in Ihr App-Projekt und machen Sie so etwas

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

}
1
loretoparisi

Hier ist eine Lösung mit 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
    }

}
1
Alex

Swift + Property.

Ich bevorzuge eine Hex-Darstellung als Eigenschaft (wie bytes und description-Eigenschaften):

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

Idee wird von diesem entlehnt Antwort

0
Avt

Ändern Sie %08x in %08X, um Großbuchstaben zu erhalten.

0
Dan Reese
@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
Ramesh