NSData를 6진수 문자열로 직렬화하는 가장 좋은 방법
NSData 객체를 16진수 문자열로 직렬화할 수 있는 nice-cococoa 방법을 찾고 있습니다.그 아이디어는 장치를 직렬화하는 것입니다.서버로 보내기 전에 알림에 사용되는 토큰입니다.
저는 다음과 같은 구현을 가지고 있지만, 그것을 하기 위해서는 더 짧고 더 좋은 방법이 있을 것이라고 생각합니다.
+ (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진수 NSS 문자열을 반환합니다. 여기서 데이터는 임의의 길이일 수 있습니다.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이 NSData의 포맷을 변경할지 여부도 알 수 없습니다.-description
미래에.
참고: 이 답변에 있는 코드에 대한 라이센스에 대해 사람들에게 문의하도록 했습니다.저는 이 답변에 게시한 코드의 저작권을 퍼블릭 도메인에 바칩니다.
16진수 문자열을 생성하기 위한 고도로 최적화된 NSData 범주 방법은 다음과 같습니다.@Dave Gallager의 대답은 상대적으로 작은 크기에 대해서는 충분하지만 대용량 데이터에 대해서는 메모리와 CPU 성능이 저하됩니다.제 아이폰 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에 허용되는 메커니즘으로 간주되어서는 안 됩니다.해당 속성은 설명용이며 언제든지 변경할 수 있습니다.참고로 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
다음은 더 빠른 변환 방법입니다.
BenchMark(100회 반복되는 1024바이트 데이터 변환 평균 시간):
데이브 갤러거: ~8.070ms
NS Programmer : ~0.077ms
피터: ~0.031ms
One : ~ 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 버전
라이너 하나:
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, 기수: 16)을 문자열(형식: "%02x", $0)로 변경했습니다.
피터의 대답은 스위프트에게 전해졌습니다.
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 "";
}
저는 이 문제를 해결해야 했고, 여기서 답이 매우 유용하다는 것을 알게 되었지만, 저는 성과에 대해 걱정하고 있습니다.이러한 답변의 대부분은 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;
}
이렇게 하면 전체 결과에 대해 문자열에 공간을 미리 할당하고 열거형 ByteRangesUsingBlock을 사용하여 NSData 내용을 복사하는 것을 방지할 수 있습니다.형식 문자열에서 X를 x로 변경하면 소문자 16진수가 사용됩니다.바이트 사이에 구분 기호를 사용하지 않으려면 문을 줄일 수 있습니다.
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 apasterString]을(를) 사용하여 데이터 설명의 문자를 대문자로 표시할 수 있습니다.
NSData를 NSString으로 직렬화/직렬화하는 더 나은 방법은 Google Toolbox for Mac Base64 인코더/디코더를 사용하는 것입니다.패키지 Foundation에서 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
대문자를 얻기 위해.
스위프트 + 속성.
합니다.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
하지만 그건 취향의 문제입니다.
언급URL : https://stackoverflow.com/questions/1305225/best-way-to-serialize-an-nsdata-into-a-hexadeximal-string
'source' 카테고리의 다른 글
Firebase Auth ID 토큰에 잘못된 "aud" 클레임이 있습니다. (0) | 2023.06.13 |
---|---|
UIPageViewController에서 프로그래밍 방식으로 페이지를 넘길 수 있습니까? (0) | 2023.06.13 |
ASP.NET MVC 상대 경로 (0) | 2023.06.13 |
tsconfig.json에서 'jsx' 속성의 용도는 무엇입니까? (0) | 2023.06.13 |
UIScrollView에서 스크롤 표시기를 숨길 수 있는 방법이 있습니까? (0) | 2023.06.13 |