NSString (шестнадцатеричный) в байтах

Есть ли какой-либо метод в Objective-C, который преобразует шестнадцатеричную строку в байты? Например @"1156FFCD3430AA22" для unsigned char array {0x11, 0x56, 0xFF, ...},

7 ответов

Самая быстрая реализация категории NSString, о которой я только мог подумать (несколько примеров):

- (NSData *)dataFromHexString {
    const char *chars = [self UTF8String];
    int i = 0, len = self.length;

    NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
    char byteChars[3] = {'\0','\0','\0'};
    unsigned long wholeByte;

    while (i < len) {
        byteChars[0] = chars[i++];
        byteChars[1] = chars[i++];
        wholeByte = strtoul(byteChars, NULL, 16);
        [data appendBytes:&wholeByte length:1];
    }

    return data;
}

Это почти в 8 раз быстрее, чем решение Wookay. NSScanner довольно медленный.

@interface NSString (NSStringHexToBytes)
-(NSData*) hexToBytes ;
@end



@implementation NSString (NSStringHexToBytes)
-(NSData*) hexToBytes {
  NSMutableData* data = [NSMutableData data];
  int idx;
  for (idx = 0; idx+2 <= self.length; idx+=2) {
    NSRange range = NSMakeRange(idx, 2);
    NSString* hexStr = [self substringWithRange:range];
    NSScanner* scanner = [NSScanner scannerWithString:hexStr];
    unsigned int intValue;
    [scanner scanHexInt:&intValue];
    [data appendBytes:&intValue length:1];
  }
  return data;
}
@end



/// example
unsigned char bytes[] = { 0x11, 0x56, 0xFF, 0xCD, 0x34, 0x30, 0xAA, 0x22 };
NSData* expectedData = [NSData dataWithBytes:bytes length:sizeof(bytes)];
NSLog(@"data %@", [@"1156FFCD3430AA22" hexToBytes]);
NSLog(@"expectedData isEqual:%d", [expectedData isEqual:[@"1156FFCD3430AA22" hexToBytes]]);

scanHexInt: и аналогичные методы NSScanner может быть полезным для выполнения того, что вы хотите, но вам, вероятно, нужно сначала разбить строку на более мелкие куски, и в этом случае выполнение перевода вручную может быть проще, чем использование NSScanner,

Не так, как вы это делаете. Вам нужно написать свой собственный метод, который будет принимать каждые два символа, интерпретировать их как in t и сохранять их в массиве.

Модифицированный подход,

/* Converts a hex string to bytes.
 Precondition:
 . The hex string can be separated by space or not.
 . the string length without space or 0x, must be even. 2 symbols for one byte/char
 . sample input: 23 3A F1 OR 233AF1, 0x23 0X231f 2B
 */

+ (NSData *) dataFromHexString:(NSString*)hexString
{
    NSString * cleanString = [Util cleanNonHexCharsFromHexString:hexString];
    if (cleanString == nil) {
        return nil;
    }

    NSMutableData *result = [[NSMutableData alloc] init];

    int i = 0;
    for (i = 0; i+2 <= cleanString.length; i+=2) {
        NSRange range = NSMakeRange(i, 2);
        NSString* hexStr = [cleanString substringWithRange:range];
        NSScanner* scanner = [NSScanner scannerWithString:hexStr];
        unsigned int intValue;
        [scanner scanHexInt:&intValue];
        unsigned char uc = (unsigned char) intValue;
        [result appendBytes:&uc length:1];
    }
    NSData * data = [NSData dataWithData:result];
    [result release];
    return data;
}

/* Clean a hex string by removing spaces and 0x chars.
 . The hex string can be separated by space or not.
 . sample input: 23 3A F1; 233AF1; 0x23 0x3A 0xf1
 */

+ (NSString *) cleanNonHexCharsFromHexString:(NSString *)input
{
    if (input == nil) {
        return nil;
    }

    NSString * output = [input stringByReplacingOccurrencesOfString:@"0x" withString:@""
                                    options:NSCaseInsensitiveSearch range:NSMakeRange(0, input.length)];
    NSString * hexChars = @"0123456789abcdefABCDEF";
    NSCharacterSet *hexc = [NSCharacterSet characterSetWithCharactersInString:hexChars];
    NSCharacterSet *invalidHexc = [hexc invertedSet];
    NSString * allHex = [[output componentsSeparatedByCharactersInSet:invalidHexc] componentsJoinedByString:@""];
    return allHex;
}

Первая попытка в Swift 2.2:

func hexStringToBytes(hexString: String) -> NSData? {
    guard let chars = hexString.cStringUsingEncoding(NSUTF8StringEncoding) else { return nil}
    var i = 0
    let length = hexString.characters.count

    let data = NSMutableData(capacity: length/2)
    var byteChars: [CChar] = [0, 0, 0]

    var wholeByte: CUnsignedLong = 0

    while i < length {
        byteChars[0] = chars[i]
        i+=1
        byteChars[1] = chars[i]
        i+=1
        wholeByte = strtoul(byteChars, nil, 16)
        data?.appendBytes(&wholeByte, length: 1)
    }

    return data
}

Или как расширение для String:

extension String {

    func dataFromHexString() -> NSData? {
        guard let chars = cStringUsingEncoding(NSUTF8StringEncoding) else { return nil}
        var i = 0
        let length = characters.count

        let data = NSMutableData(capacity: length/2)
        var byteChars: [CChar] = [0, 0, 0]

        var wholeByte: CUnsignedLong = 0

        while i < length {
            byteChars[0] = chars[i]
            i+=1
            byteChars[1] = chars[i]
            i+=1
            wholeByte = strtoul(byteChars, nil, 16)
            data?.appendBytes(&wholeByte, length: 1)
        }

        return data
    }
}

Это непрерывная работа, но пока она работает хорошо.

Дальнейшую оптимизацию и более глубокое обсуждение можно найти в Code Review.

Несколько решений возвращает неправильное значение, если строка, подобная этой "DBA"

Правильные данные для строки "DBA" - "\ x0D \ xBA" (значение int: 3514)

если вы получили данные не такие, как "\ x0D \ xBA", это означает, что вы получили неправильный байт, потому что значение будет другим, например, вы получили данные, подобные этой "\ xDB \ x0A", значение int равно 56074

Вот переписать решение:

+ (NSData *)dataFromHexString:(NSString *) string {
    if([string length] % 2 == 1){
        string = [@"0"stringByAppendingString:string];
    }

    const char *chars = [string UTF8String];
    int i = 0, len = (int)[string length];

    NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
    char byteChars[3] = {'\0','\0','\0'};
    unsigned long wholeByte;

    while (i < len) {
        byteChars[0] = chars[i++];
        byteChars[1] = chars[i++];
        wholeByte = strtoul(byteChars, NULL, 16);
        [data appendBytes:&wholeByte length:1];
    }
    return data;

}
Другие вопросы по тегам