문제

NSDATA 객체를 16 진수로 직렬화하는 멋진 코코아 방법을 찾고 있습니다. 아이디어는 내 서버로 전송하기 전에 알림에 사용 된 DeviceToken을 직렬화하는 것입니다.

다음과 같은 구현이 있지만 더 짧고 더 좋은 방법이 있어야한다고 생각합니다.

+ (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에 적용되는 범주입니다. NSData를 나타내는 16 진 NSString을 반환합니다. 여기서 데이터는 길이가 될 수 있습니다. 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 및>를 벗기십시오. 스트리핑 캐릭터는 너무 "해킹"이라고 느낍니다. 또한 Apple이 NSData의 서식을 변경할 것인지는 결코 알 수 없습니다. -description 미래에.

노트: 이 답변에서 사람들이 코드 라이센스에 대해 저에게 연락하도록했습니다. 이 답변에 공개 도메인에 게시 한 코드에 저작권을 전념합니다.

다른 팁

다음은 고도로 최적화 된 것입니다 NSDATA 카테고리 육각 문자열을 생성하는 메소드. @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 @"";
}

NSData의 설명 속성을 사용하여 문자열을 인코딩하는 HEX를위한 허용 가능한 메커니즘으로 간주되어서는 안됩니다. 그 속성은 설명을위한 것이며 언제든지 변경 될 수 있습니다. 참고로, Pre-Ios에서 NSDATA 설명 속성은 16 진 양식으로 데이터를 반환하지 않았습니다.

솔루션을 구하는 것에 대해 죄송하지만 데이터 직렬화 이외의 다른 것을위한 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, radix : 16)을 문자열로 변경했습니다 (형식 : "%02x", $ 0).

전환을하는 더 빠른 방법은 다음과 같습니다.

벤치 마크 (1024 바이트 데이터 변환의 평균 시간은 100 회 반복) :

데이브 갤러거 : ~ 8.070ms
NSPROGRAMMER : ~ 0.077ms
피터 : ~ 0.031 ms
이것은 ~ 0.017ms입니다

@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

베드로의 대답은 스위프트로 이어졌습니다

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

스위프트 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;
}

이 전체 결과를 위해 문자열의 공간을 사전 할당하고 EnumerateByterangesusingBlock을 사용하여 NSData 내용을 복사하지 않습니다. 형식 문자열에서 x를 x로 변경하면 소문자 헥스 숫자가 사용됩니다. 바이트 사이의 분리기를 원하지 않으면 진술을 줄일 수 있습니다.

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 UpperCasestring]을 사용하여 데이터 설명에서 문자를 대문자로 만들 수 있습니다.

nsdata를 nsstring으로 직렬화/사형화하는 더 좋은 방법은 Mac 용 Google 도구 상자 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 자본 캐릭터를 얻으려면.

스위프트 + 속성.

나는 16 진수를 속성으로 선호합니다 ( 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