2010-02-26 13 views
10

całkiem nowy programista iPhone'a tutaj. Budowanie aplikacji do wysyłania poleceń RS232 do urządzenia oczekującego ich przez gniazdo TCP/IP. Mam comms part, i mogę wysyłać polecenia ASCII w porządku. Jest to polecenie kodu szesnastkowego, z którym mam problem.Konwertuj ciąg danych szesnastkowych na NSData w Objective C (kakao)

Więc powiedzmy, że mam następujące dane hex do wysłania (w tym formacie):

\ X1C \ x02d \ x00 \ x00 \ x00 \ xFF \ x7F

Jak przekonwertować to do Obiekt NSData, którego oczekuje moja metoda wysyłania?

Oczywiście to nie działa na tych danych hex (ale nie dla standardowych poleceń ASCII):

NSString *commandascii; 
NSData *commandToSend; 
commandascii = @"\x1C\x02d\x00\x00\x00\xFF\x7F"; 
commandToSend = [commandascii dataUsingEncoding:NSStringEncoding]; 

Na początek, niektóre \ x kody hex to znaki ewakuacyjne i otrzymuję „wejście konwersja zatrzymana ... "ostrzeżenie podczas kompilacji w XCode. Oczywiście NSStringEncoding nie jest odpowiedni dla tego łańcucha szesnastkowego.

Pierwszy problem polega na tym, jak zapisać ciąg szesnastkowy, a następnie przekonwertować go na NSData.

Wszelkie pomysły?

+0

Dzięki za wszystkie odpowiedzi, masz wszystko było wielką pomoc i I Dużo się nauczyłem! Zbudowałem powyższy kod, który jest niezamierzonym połączeniem kilku odpowiedzi. Działa to w przypadku ciągów takich jak "00 3c 5f 22 ef 00 00 ff", który okazuje się bardziej odpowiedni niż styl \ x. Jeszcze raz dziękuję za pomoc! –

Odpowiedz

30

Kod heksadecymalny w NSStrings, taki jak "00 05 22 1C EA 01 00 FF". "command" to hex NSString.

command = [command stringByReplacingOccurrencesOfString:@" " withString:@""]; 
NSMutableData *commandToSend= [[NSMutableData alloc] init]; 
unsigned char whole_byte; 
char byte_chars[3] = {'\0','\0','\0'}; 
for (int i = 0; i < ([command length]/2); i++) { 
    byte_chars[0] = [command characterAtIndex:i*2]; 
    byte_chars[1] = [command characterAtIndex:i*2+1]; 
    whole_byte = strtol(byte_chars, NULL, 16); 
    [commandToSend appendBytes:&whole_byte length:1]; 
} 
NSLog(@"%@", commandToSend); 
+0

byłoby miło, gdybyś skomentował niektóre i uczynił je bardziej ogólnymi, więc nie musisz używać 8-sza bajtów w ciągu znaków. poza tym miło thx –

+1

to działało dla mnie. Jeśli masz ogólny łańcuch szesnastkowy (a nie 8-szesnastkowy-bajtowy, jak w przykładzie), użyj [długość polecenia]/2 zamiast 8 w pętli for. – Fmessina

+0

Dzięki ... jego działa .. –

-3

Dane szesnastkowe to tylko bajty w pamięci, myśli się o nich jako o łańcuchach, ponieważ tak to widzicie, ale mogą one reprezentować wszystko. Spróbuj: (wpisane w przeglądarce, mogą zawierać błędy)

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

[hexData appendBytes: 0x1C]; 
[hexData appendBytes: 0x02D]; 

etc ...

+0

Wygląda bardziej na to, co chcę, dzięki Bruce. Nie działa jednak, po naprawieniu składni (powinno być appendbytes: length :) Otrzymuję "ostrzeżenie: przekazanie argumentu 1 z" appendBytes: length: 'tworzy wskaźnik z liczby całkowitej bez rzutowania " Jakieś pomysły? –

+3

'appendBytes: length:' oczekuje * wskaźnika * na bajty, a także na liczbę bajtów, które dodajesz. – dreamlax

9

Oto przykład dekoder realizowane w kategorii na NSString.

#import <stdio.h> 
#import <stdlib.h> 
#import <string.h> 

unsigned char strToChar (char a, char b) 
{ 
    char encoder[3] = {'\0','\0','\0'}; 
    encoder[0] = a; 
    encoder[1] = b; 
    return (char) strtol(encoder,NULL,16); 
} 

@interface NSString (NSStringExtensions) 
- (NSData *) decodeFromHexidecimal; 
@end 

@implementation NSString (NSStringExtensions) 

- (NSData *) decodeFromHexidecimal; 
{ 
    const char * bytes = [self cStringUsingEncoding: NSUTF8StringEncoding]; 
    NSUInteger length = strlen(bytes); 
    unsigned char * r = (unsigned char *) malloc(length/2 + 1); 
    unsigned char * index = r; 

    while ((*bytes) && (*(bytes +1))) { 
     *index = strToChar(*bytes, *(bytes +1)); 
     index++; 
     bytes+=2; 
    } 
    *index = '\0'; 

    NSData * result = [NSData dataWithBytes: r length: length/2]; 
    free(r); 

    return result; 
} 

@end 
+0

Dzięki xyzzy, ale to się nie kompiluje - ma problemy z safeMalloc, strToChar i safeStrLen.Czy muszę włączyć/importować coś, aby te funkcje działały? –

+0

Ah, racja. Możesz zastąpić te połączenia w/malloc i strlen. Zaktualizuję odpowiedź. – xyzzycoder

+0

Należy skompilować teraz. – xyzzycoder

4

Jeśli można ciężko kodować dane hex:

const char bytes[] = "\x00\x12\x45\xAB"; 
size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0' 

NSData *data = [NSData dataWithBytes:bytes length:length];

Jeśli kod należy interpretować ciąg szesnastkowy (zakładając, że ciąg hex jest w zmiennej o nazwie inputData i lengthOfInputData jest długość inputData) :


#define HexCharToNybble(x) ((char)((x > '9') ? tolower(x) - 'a' + 10 : x - '0') & 0xF) 

int i; 

NSMutableData *data = [NSMutableData data]; 

for (i = 0; i < lengthOfInputData;) 
{ 
    char byteToAppend; 

    if (i < (lengthOfInputData - 3) && 
     inputData[i+0] == '\\' && 
     inputData[i+1] == 'x' && 
     isxdigit(inputData[i+2]) && 
     isxdigit(inputData[i+3])) 
    { 
     byteToAppend = HexCharToNybble(inputData[i+2]) << 4 + HexCharToNybble(input[i+3]); 
     i += 4; 
    } 
    else 
    { 
     byteToAppend = inputData[i]; 
     i += 1; 
    } 

    [data appendBytes:&byteToAppend length:1]; 
}
+0

Woah, to brzydkie. Przepraszam, jeśli oślepiłem każdego z tym kodem źródłowym. Wyglądało to trochę brzydko, kiedy pisałem, ale postanowiłem to ukończyć. – dreamlax

+0

Również całkowicie nietestowane. – dreamlax

+0

Dobrze, pokazując inny sposób na zrobienie tego. –

1

Jeśli chcę twardym kodu bajtów, zrobić coś takiego:

enum { numCommandBytes = 8 }; 
static const unsigned char commandBytes[numCommandBytes] = { 0x1c, 0x02, 'd', 0x0, 0x0, 0x0, 0xff, 0x7f }; 

Jeśli w czasie wykonywania otrzymujesz bajty z odwróceniem ukośnika odwróconego, spróbuj the strunvis function.

Oczywiście to nie działa na tych danych hex (ale nie dla standardowych poleceń ASCII):

NSString *commandascii; 
NSData *commandToSend; 
commandascii = @"\x1C\x02d\x00\x00\x00\xFF\x7F"; 
commandToSend = [commandascii dataUsingEncoding:NSStringEncoding]; 

Na początek, niektóre \x kody szesnastkowe są uciec znaków, i otrzymuję " konwersja wejścia zatrzymana ... "ostrzeżenie podczas kompilacji w XCode. Oczywiście NSStringEncoding nie jest odpowiedni dla tego łańcucha szesnastkowego.

Po pierwsze, jest to Xcode, z małą literą c.

Po drugie, NSStringEncoding jest typem, a nie identyfikatorem kodowania. Ten kod nie powinien się w ogóle kompilować.

Co więcej, ukośnik odwrotny nie jest kodowaniem; w rzeczywistości jest w dużej mierze niezależny od kodowania. Odwrotny ukośnik i "x" są znakami, a nie bajtami, co oznacza, że ​​muszą być zakodowane do (i odkodowane) bajtów, co jest zadaniem kodowania.

2

To jest stary temat, ale chciałbym dodać kilka uwag.

• Skanowanie ciągu znaków za pomocą [NSString characterAtIndex] nie jest zbyt wydajne. Pobierz ciąg znaków C w UTF8, a następnie zeskanuj go za pomocą *char++ jest znacznie szybszy.

• Lepiej jest rozdzielić pojemność na NSMutableData, aby uniknąć czasochłonnej zmiany wielkości bloku. Myślę NSData jest jeszcze lepiej (patrz następny punkt)

• Zamiast tworzyć NSData za pomocą malloc, a następnie [NSData dataWithBytes] i wreszcie wolne, używać malloc i [NSData dataWithBytesNoCopy:length:freeWhenDone:]

unika również działanie pamięci (realokacji, kopiowania za darmo) . FreeWhenDone boolean mówi NSData, aby przejął na własność blok pamięci i zwolnił go, gdy zostanie zwolniony.

• Oto funkcja, którą muszę przekonwertować ciągi heksadecymalne na bloki bajtów. Nie ma wiele sprawdzania błędów w łańcuchu wejściowym, ale alokacja jest testowana.

Formatowanie wejściowego ciągu znaków (np. Usuń 0x, spacje i znaki interpunkcyjne) lepiej wychodzi z funkcji konwersji. Po co tracić czas na dodatkowe przetwarzanie, jeśli jesteśmy pewni, że dane wejściowe są prawidłowe.

+(NSData*)bytesStringToData:(NSString*)bytesString 
{ 
    if (!bytesString || !bytesString.length) return NULL; 
    // Get the c string 
    const char *scanner=[bytesString cStringUsingEncoding:NSUTF8StringEncoding]; 
    char twoChars[3]={0,0,0}; 
    long bytesBlockSize = formattedBytesString.length/2; 
    long counter = bytesBlockSize; 
    Byte *bytesBlock = malloc(bytesBlockSize); 
    if (!bytesBlock) return NULL; 
    Byte *writer = bytesBlock; 
    while (counter--) { 
     twoChars[0]=*scanner++; 
     twoChars[1]=*scanner++; 
     *writer++ = strtol(twoChars, NULL, 16); 
    } 
    return[NSData dataWithBytesNoCopy:bytesBlock length:bytesBlockSize freeWhenDone:YES]; 
} 
0

Inny sposób na zrobienie tego.

-(NSData *) dataFromHexString:(NSString *) hexstr 
{ 
    NSMutableData *data = [[NSMutableData alloc] init]; 
    NSString *inputStr = [hexstr uppercaseString]; 

    NSString *hexChars = @"ABCDEF"; 

    Byte b1,b2; 
    b1 = 255; 
    b2 = 255; 
    for (int i=0; i<hexstr.length; i++) { 
     NSString *subStr = [inputStr substringWithRange:NSMakeRange(i, 1)]; 
     NSRange loc = [hexChars rangeOfString:subStr]; 

     if (loc.location == NSNotFound) continue; 

     if (255 == b1) { 
      b1 = (Byte)loc.location; 
     }else { 
      b2 = (Byte)loc.location; 

      //Appending the Byte to NSData 
      Byte *bytes = malloc(sizeof(Byte) *1); 
      bytes[0] = ((b1<<4) & 0xf0) | (b2 & 0x0f); 
      [data appendBytes:bytes length:1]; 

      b1 = b2 = 255; 
     } 
    } 

    return data; 
} 
+0

Jest to całkiem nieefektywny algorytm, używając zarówno 'subStringWithRange:' jak i 'rangeOfString:' ... wydaje się niepotrzebny. Ponadto, myślę, że byłoby lepiej, aby spróbować czytać dwa bajty na raz, zamiast korzystać z komputera z mini-stanem. – dreamlax

-1

Wiem, że to bardzo stary wątek, ale nie jest to schemat kodowania w Objective C, który można łatwo przekonwertować ciąg kodów hex do znaków ASCII.

1) wyjąć \x z łańcucha i z obecnie utrzymanie przestrzeni w ciągu zaledwie przekonwertować ciąg NSData używając:

[[NSData alloc] initWithData:[stringToBeConverted dataUsingEncoding:NSASCIIStringEncoding]]; 
+0

Nie. OP nie chce przekształcić samego ciągu w dane. Chce przekształcić ** szesnastkową reprezentację danych z powrotem na same dane. –