2010-02-26 7 views
10

bastante nuevo desarrollador de iPhone aquí. Creación de una aplicación para enviar comandos RS232 a un dispositivo que los espera a través de una conexión de socket TCP/IP. Tengo la parte de comunicaciones baja, y puedo enviar comandos ASCII bien. Son los comandos del código hexadecimal con los que estoy teniendo problemas.Convertir cadena de datos hexadecimales a NSData en Objective C (cocoa)

lo que permite decir que tengo los siguientes datos hexadecimales para enviar (en este formato):

\ X1c \ x02d \ x00 \ x00 \ x00 \ xFF \ x7F

¿Cómo puedo convertir esto en una Objeto NSData, que mi método de envío espera?

Obviamente, esto no funciona para estos datos hexagonales (pero lo hace para los comandos estándar ASCII):

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

Para empezar, algunos de los \ x códigos hexadecimales son los caracteres de escape, y me da un "insumo conversión detenida ... "advertencia al compilar en XCode. Y NSStringEncoding obviamente no es el adecuado para esta cadena hexadecimal tampoco.

Así que el primer problema es cómo almacenar esta cadena hexadecimal, supongo, y cómo convertir a NSData.

¿Alguna idea?

+0

Gracias por todas las respuestas, todos han sido de gran ayuda y yo ¡he aprendido mucho! Construí el código anterior, que es un poco una amalgama involuntaria de algunas de las respuestas. Esto funciona para cadenas como "00 3c 5f 22 ef 00 00 ff" que resulta que puedo obtener en lugar del estilo \ x. ¡Gracias de nuevo por toda la ayuda! –

Respuesta

30

Código para hex. En NSStrings como "00 05 22 1C EA 01 00 FF". 'comando' es el 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

sería bueno si usted comentó un poco y lo hizo más genérico, por lo que no necesita utilizar un 8-hex bytes en la cadena. de lo contrario, bueno, thx –

+1

funcionó para mí. Si tiene una cadena hexadecimal genérica (y no un 8-hex-bytes como en el ejemplo), use [longitud de comando]/2 en lugar de 8 en el ciclo for. – Fmessina

+0

Gracias ... su funcionamiento .. –

-3

Los datos hexadecimales son solo bytes en la memoria, lo piensas como una cadena porque así es como lo ves, pero podrían representar cualquier cosa. Probar: (a máquina en el navegador, puede contener errores)

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

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

etc ...

+0

Esto se parece más a lo que estoy buscando, gracias Bruce. No funciona, sin embargo, después de arreglar la sintaxis (debe ser appendbytes: length :) Obtengo una "advertencia: pasando el argumento 1 de 'appendBytes: length:' hace puntero desde un entero sin un molde" ¿Alguna idea? –

+3

'appendBytes: length:' espera un * puntero * a los bytes, así como el número de bytes que está agregando. – dreamlax

9

He aquí un ejemplo de decodificador implementado en una categoría en 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

Gracias xyzzy, aunque esto no compila, tiene problemas con safeMalloc, strToChar y safeStrLen.¿Debo incluir/importar algo para que funcionen estas funciones? –

+0

Ah, a la derecha. Puede reemplazar estas llamadas w/malloc y strlen. Actualizaré la respuesta. – xyzzycoder

+0

Debe compilar ahora. – xyzzycoder

4

Si puede codificar los datos 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];

Si el código debe interpretar la cadena hexadecimal (suponiendo que la cadena hexadecimal está en una variable llamada inputData y lengthOfInputData es la longitud de 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, eso es feo. Lo siento si he cegado a alguien con ese código fuente. Se veía un poco feo mientras lo escribía pero decidí terminarlo de todos modos. – dreamlax

+0

También, no probado por completo. – dreamlax

+0

bueno, mostrando otra manera de hacerlo. –

1

Si quiero codificar los bytes, hago algo como esto:

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

Si está obteniendo estos bytes escapados en la barra invertida en tiempo de ejecución, intente the strunvis function.

Obviamente, esto no funciona para estos datos hexagonales (pero lo hace para los comandos estándar ASCII):

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

Para empezar, algunos de los \x códigos hexadecimales son caracteres de escape, y me sale un " conversión de entrada detenida ... "advertencia al compilar en XCode. Y NSStringEncoding obviamente no es el adecuado para esta cadena hexadecimal tampoco.

Primero, es Xcode, con una c minúscula.

En segundo lugar, NSStringEncoding es un tipo, no un identificador de codificación. Ese código no debe compilar en absoluto.

Más al punto, backslash-escaping no es una codificación; de hecho, es en gran medida independiente de la codificación. La barra invertida y 'x' son caracteres, no bytes, lo que significa que deben codificarse (y decodificarse) a partir de bytes, que es el trabajo de una codificación.

2

Este es un tema antiguo, pero me gustaría agregar algunas observaciones.

• Escanear una cadena con [NSString characterAtIndex] no es muy eficiente. Obtenga la cadena C en UTF8, luego escanearla usando un *char++ es mucho más rápido.

• Es mejor asignar NSMutableData con capacidad, para evitar el cambio de tamaño del bloque. Creo NSData es incluso mejor (véase el siguiente punto)

• En lugar de crear NSData usando malloc, a continuación, [NSData dataWithBytes] y finalmente libre, el uso de malloc y [NSData dataWithBytesNoCopy:length:freeWhenDone:]

También evita el funcionamiento de la memoria (reasignar, copiar, gratis) . El booleano freeWhenDone le dice a NSData que tome posesión del bloque de memoria y lo libere cuando se lo libere.

• Aquí está la función que tengo que convertir cadenas hexagonales en bloques de bytes. No hay muchos errores al verificar la cadena de entrada, pero se evalúa la asignación.

El formato de la cadena de entrada (como eliminar 0x, espacios y signos de puntuación) es mejor fuera de la función de conversión. ¿Por qué perderíamos algo de tiempo haciendo un procesamiento adicional si estamos seguros de que la entrada es correcta?

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

Otra forma de hacerlo.

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

Este es un algoritmo bastante ineficiente, que usa tanto 'subStringWithRange:' como 'rangeOfString:' ... parece innecesario. Además, creo que sería mejor tratar de leer dos bytes a la vez en lugar de utilizar una mini máquina de estado. – dreamlax

-1

Sé que esto es un hilo muy antiguo, pero hay un esquema de codificación en C Objetivo que puede convertir fácilmente su cadena de códigos hexadecimales en caracteres ASCII.

1) eliminar el \x de la cuerda y con espacios a cabo manteniendo en la cadena acaba de convertir la cadena en NSData usando:

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

No. El OP no desea convertir la cadena en datos. Él quiere convertir la ** representación hexadecimal ** de los datos a los datos mismos. –

Cuestiones relacionadas