2010-03-23 29 views
17

Czy istnieje metoda w Objective-C, która konwertuje ciąg szesnastkowy na bajty? Na przykład @"1156FFCD3430AA22" do unsigned char array {0x11, 0x56, 0xFF, ...}.NSString (hex) do bajtów

Odpowiedz

2

Nie w taki sposób, w jaki to robisz. Musisz napisać własną metodę, aby wykonać co dwa znaki, zinterpretować je jako int i zapisać w tablicy.

3

W scanHexInt: i podobne metody NSScanner mogą być pomocne w czynieniu tego, co chcesz, ale ja prawdopodobnie trzeba przerwać ciąg na mniejsze kawałki po pierwsze, w takim przypadku robi tłumaczenie ręcznie może być prostsze niż przy użyciu NSScanner.

21
@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]]); 
+5

mnie poprawić, jeśli się mylę, ale myślę, że [appendBytes danych: & długość intValue: 1] ma jakieś kłopoty. Typ unsigned int ma szerokość co najmniej czterech znaków (co najmniej 32-bitowy). & intValue jest wskaźnikiem do pierwszego z czterech. Oznacza to, że na małym systemie endianów & intValue będzie słusznie wskaźnikiem do właśnie zeskanowanego znaku. Ale na dużych architekturach endianowych zawsze wskaże on wartość zerową, ponieważ wartość jest przechowywana jako ostatnia z czterech znaków. Zasadniczo oznacza to, że ten kod będzie działał na komputerach Mac i łamie się na iOS. – Trenskow

+2

Ta odpowiedź ma wiele wad. Nie tylko traktuje 'int' tak, jakby był bajtem (co jest niemożliwe zgodnie ze standardem C), ale tylko nadmiernie generuje wiele podłańcuchów, zamiast tylko przechodzić przez' const char * 'można uzyskać za pomocą 'UTF8String' ... –

1

zmodyfikowane podejście,

/* 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 = @"abcdefABCDEF"; 
    NSCharacterSet *hexc = [NSCharacterSet characterSetWithCharactersInString:hexChars]; 
    NSCharacterSet *invalidHexc = [hexc invertedSet]; 
    NSString * allHex = [[output componentsSeparatedByCharactersInSet:invalidHexc] componentsJoinedByString:@""]; 
    return allHex; 
} 
39
kategoria realizacja

Najszybsza NSString że mogę myśleć (koktajl kilka przykładów):

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

Jest blisko do 8 razy szybciej niż rozwiązania wookay za . NSScanner działa dość wolno.

+3

+1 to wydaje się być lepsze - nie tylko szybciej, ale jest to ** właściwie poprawne.** –

+0

Bardzo dobra implementacja. –

+0

Dobra implementacja, ale jeśli potrzebujesz sprawdzania błędów, musisz ustawić 'errno' na 0 przed wywołaniem' strtoul' i przeczytaniem go później. – Joe

0

Pierwsza próba w 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 
} 

Albo, jako przedłużenie na wyrażenie:

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

To jest postęp prac w sposób ciągły, ale wydaje się działać dobrze do tej pory.

Dalsze optymalizacje i bardziej szczegółową dyskusję można znaleźć pod adresem Code Review.

+0

Czy to nie jest tylko szybkie tłumaczenie [tej odpowiedzi] (http://stackoverflow.com/a/17511588/865175)? –

0

Kilka rozwiązanie jest zwracany błędną wartość, jeśli ciąg lubię tego "DBA"

poprawne dane dla "DBA" string jest "\ x0D \ Xba" (int value: 3514)

jeśli masz dane nie podoba „\ x0D \ Xba” to znaczy, że masz zły bajt, ponieważ wartość będzie inny, na przykład masz dane jak ten „\ XDB \ x0A”int wartość wynosi 56.074

Oto przepisać rozwiązanie:

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

}