iphone json - Der beste Weg, um ein NSData in eine hexadeximale Zeichenfolge zu serialisieren





serialize c# (13)


Peters Antwort portierte 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 "";
}

Ich suche nach einem netten Kakaoweg, um ein NSData-Objekt in eine hexadezimale Zeichenfolge zu serialisieren. Die Idee besteht darin, das für Benachrichtigungen verwendete deviceToken zu serialisieren, bevor es an meinen Server gesendet wird.

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

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



Funktionale Swift-Version

Einzeiler:

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

Hier ist in einer wiederverwendbaren und selbstdokumentierenden 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 verwenden Sie reduce("", combine: +) anstelle von joinWithSeparator("") , um von Ihren Peers als funktionaler Master angesehen zu werden.

Edit: Ich änderte String ($ 0, Radix: 16) zu String (Format: "% 02x", $ 0), weil eine Ziffernzahl benötigt, um eine Polsterung Null zu haben




Eine bessere Möglichkeit zum Serialisieren / Deserialisieren von NSData in NSString ist die Verwendung des Google Base Tool Encoders / Decoders für Mac Base64. Ziehen Sie einfach die Dateien GTMBase64.m, GTMBase64.he GTMDefines.h aus dem Paket Foundation in das App-Projekt und machen Sie etwas ähnliches

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

}



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

Es tut mir leid, dass ich auf die Lösung gestoßen bin, aber es ist wichtig, die Energie zu nutzen, um sie serialisieren zu können, ohne eine API zu deaktivieren, die für etwas anderes als 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



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
    }

}



Schnell + Eigentum.

Ich bevorzuge eine hexadezimale Darstellung als Eigenschaft (das gleiche wie bei den Eigenschaften für bytes und 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("")
    }
}

Die Idee ist dieser answer entlehnt




Hier ist eine hoch optimierte NSData-Kategorie- Methode zum Erzeugen einer Hex-Zeichenfolge. Während die Antwort von @Dave Gallagher für eine relativ kleine Größe ausreicht, verschlechtern sich Speicher- und CPU-Leistung für große Datenmengen. Ich profilierte dies mit einer 2MB-Datei auf meinem iPhone 5. Zeitvergleich war 0,05 vs 12 Sekunden. Der Speicherbedarf ist mit dieser Methode vernachlässigbar, während die andere Methode den Heap auf 70 MB erhöht!

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



Hier können Sie die Konvertierung schneller durchführen:

BenchMark (durchschnittliche Zeit für eine 1024-Byte-Datenkonvertierung, die 100 Mal wiederholt wird):

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



Ich brauchte eine Antwort, die für Strings mit variabler Länge funktionieren würde, also 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.




Dies ist eine Kategorie, die auf NSData angewendet wird, 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 + Konvertierung.h

#import <Foundation/Foundation.h>

@interface NSData (NSData_Conversion)

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

@end

NSData + Konvertierung.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

Verwendung:

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

Dies ist "wahrscheinlich" besser als den Aufruf von [someData description] und dann das [someData description] der Leerzeichen, <s und> s. Stripping-Charaktere fühlen sich einfach zu "hacky" an. Außerdem weiß man nie, ob Apple die Formatierung der NSData- -description in Zukunft -description wird.

HINWEIS: Ich habe in dieser Antwort Leute angesprochen, die sich mit der Lizenzierung des Codes befassen. Hiermit widme ich mein Urheberrecht dem Code, den ich in dieser Antwort veröffentlicht habe.




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 großen Mengen aus NSData. Daher schrieb ich Folgendes, um die Konvertierung mit geringem Overhead 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 das Kopieren der NSData-Inhalte mithilfe von enumerateByteRangesUsingBlock wird vermieden. Wenn Sie in der Formatzeichenfolge das X in ein x ändern, werden Hexadezimalziffern in Kleinbuchstaben verwendet. Wenn Sie kein Trennzeichen zwischen den Bytes möchten, können Sie die Anweisung reduzieren

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

bis auf nur

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



Ändere %08x zu %08X , um %08X zu erhalten.




Für Swift:

var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
    var deviceTokenString: String = ( deviceToken.description as NSString )
    .stringByTrimmingCharactersInSet( characterSet )
    .stringByReplacingOccurrencesOfString( " ", withString: "" ) as String

println( deviceTokenString )




iphone notifications nsdata