2009-02-15 20 views
49

Estoy tratando de usar el BEncoding ObjC class para decodificar un archivo .torrent.¿Convertir bytes de NSData a NSString?

NSData *rawdata = [NSData dataWithContentsOfFile:@"/path/to/the.torrent"]; 
NSData *torrent = [BEncoding objectFromEncodedData:rawdata]; 

Cuando NSLogtorrent consigo el siguiente:

{ 
    announce = <68747470 3a2f2f74 6f727265 6e742e75 62756e74 752e636f 6d3a3639 36392f61 6e6e6f75 6e6365>; 
    comment = <5562756e 74752043 44207265 6c656173 65732e75 62756e74 752e636f 6d>; 
    "creation date" = 1225365524; 
    info =  { 
     length = 732766208; 
     name = <7562756e 74752d38 2e31302d 6465736b 746f702d 69333836 2e69736f>; 
     "piece length" = 524288; 
.... 

¿Cómo convierto el name en un NSString? He tratado ..

NSData *info = [torrent valueForKey:@"info"]; 
NSData *name = [info valueForKey:@"name"]; 
unsigned char aBuffer[[name length]]; 
[name getBytes:aBuffer length:[name length]]; 
NSLog(@"File name: %s", aBuffer); 

..which retrives los datos, pero parece tener basura adicional después de que Unicode:

File name: ubuntu-8.10-desktop-i386.iso) 

También he probado (from here) ..

NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length]/sizeof(unichar)]; 

..pero esto parece devolver un montón de caracteres aleatorios:

扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳 

El hecho de que la primera forma (como se menciona en la documentación de Apple) devuelve la mayoría de los datos correctamente, con algunos bytes adicionales me hace pensar que podría ser un error en la biblioteca BEncoding ... pero mi desconocimiento de ObjC es más es probable que la culpa ..

+0

"扵 湵 畴 㠭 ㄮ ⴰ 敤 歳 潴 ⵰㍩ 㘸 椮 潳" no es chino. Al menos no los que un hablante nativo de chino podría reconocer. –

+0

@TylerLong punto justo, editado! – dbr

Respuesta

19
NSData *torrent = [BEncoding objectFromEncodedData:rawdata]; 

Cuando NSLog torrente consigo el siguiente:

{ 
    ⋮ 
} 

Eso sería una NSDictionary, entonces, no es un NSData.

unsigned char aBuffer[[name length]]; 
[name getBytes:aBuffer length:[name length]]; 
NSLog(@"File name: %s", aBuffer); 

..which retrives los datos, pero parece tener basura Unicode adicional después de que:

File name: ubuntu-8.10-desktop-i386.iso) 

No, el nombre del archivo recuperado muy bien; simplemente lo imprimiste incorrectamente. %s toma una cadena C, que tiene terminación nula; los bytes de un objeto de datos no tienen terminación nula (son solo bytes, no necesariamente caracteres en cualquier codificación, y 0, que es nulo como un carácter, es un byte perfectamente válido).Usted tendría que destinar un personaje más, y fijar la última en la matriz a 0:

size_t length = [name length] + 1; 
unsigned char aBuffer[length]; 
[name getBytes:aBuffer length:length]; 
aBuffer[length - 1] = 0; 
NSLog(@"File name: %s", aBuffer); 

Pero nulo de terminación de los datos en un objeto NSData está mal (excepto cuando realmente hace necesita un C cuerda). Voy a llegar al camino correcto en un momento.

también he intentado [...] ..

NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length]/sizeof(unichar)]; 

..pero esto parece volver a los caracteres chinos al azar:

扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳 

Eso es porque su bytes son UTF-8 , que codifica un carácter en (generalmente) un byte.

unichar es, y stringWithCharacters:length: acepta, UTF-16. En esa codificación, un carácter es (normalmente) dos bytes. (De ahí la división por sizeof(unichar): divide el número de bytes por 2 para obtener el número de caracteres.)

Así que dijiste "aquí hay algunos datos UTF-16", y creó caracteres de cada dos bytes; se suponía que cada par de bytes eran dos caracteres, no uno, por lo que se obtenía la basura (que resultó ser principalmente ideogramas CJK).


You answered your own question bastante bien, excepto que stringWithUTF8String: es más simple que stringWithCString:encoding: para las cadenas con codificación UTF-8.

Sin embargo, cuando tiene la longitud (como lo hace cuando tiene un NSData), es aún más fácil y más apropiado usar initWithBytes:length:encoding:. Es más fácil porque no requiere datos terminados en nulo; simplemente usa la longitud que ya tienes. (No olvide liberarlo o soltarlo).

+0

stringWithBytes: length: encoding: debe ser initWithBytes: length: encoding: right? –

+0

@Brad Cupit: Sí. Lo siento por eso; Siempre me olvido de que no hay una versión 'stringWith ...' de eso (porque ¿por qué no está ?!). –

+17

peor respuesta formateada alguna vez –

6

Aha, el método NSStringstringWithCString funciona correctamente:

con los bencoding.h/.m archivos añadidos a su proyecto, la completa .m archivo:

#import <Foundation/Foundation.h> 
#import "BEncoding.h" 

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    // Read raw file, and de-bencode 
    NSData *rawdata = [NSData dataWithContentsOfFile:@"/path/to/a.torrent"]; 
    NSData *torrent = [BEncoding objectFromEncodedData:rawdata]; 

    // Get the file name 
    NSData *infoData = [torrent valueForKey:@"info"]; 
    NSData *nameData = [infoData valueForKey:@"name"]; 
    NSString *filename = [NSString stringWithCString:[nameData bytes] encoding:NSUTF8StringEncoding]; 
    NSLog(@"%@", filename); 

    [pool drain]; 
    return 0; 
} 

..y el cabo poner:

ubuntu-8.10-desktop-i386.iso 
2

En los casos en que no tengo control sobre los datos que se transforma en una cadena, tales como la lectura de la red, yo prefiero usar NSString -initWithBytes:length:encoding: por lo que no estoy depende de tener un valor NULL cadena terminada para obtener resultados definidos. Tenga en cuenta que la documentación de Apple dice que si cString no es una cadena con terminación NULL, los resultados no están definidos.

98

Ese es un punto importante que debería volver a enfatizarse, creo. Resulta que,

NSString *content = [NSString stringWithUTF8String:[responseData bytes]]; 

no es el mismo que,

NSString *content = [[NSString alloc] initWithBytes:[responseData bytes] 
       length:[responseData length] encoding: NSUTF8StringEncoding]; 

la primera espera una cadena de bytes NULL terminado, el segundo no lo hace. En los dos casos anteriores, content será NULL en el primer ejemplo si la cadena de bytes no está terminada correctamente.

+2

+1 Me estaba matando por un error que simplemente no podía entender. Gracias por el comentario profundo. Me salvaste de horas de frustración. – hyuan

+0

Excelente punto. He editado mi propia respuesta para cubrirla; es un detalle crítico, así que me disculpo por omitirlo. –

+0

@Alasdair Muchas gracias ..... –

7

Un buen enfoque rápido y sucio es utilizar el inicializador NSStringstringWithFormat para ayudarte. Una de las características menos utilizadas del formato de cadenas es la capacidad de especificar una longitud máxima de cadena al emitir una cadena.El uso de esta práctica característica le permite convertir NSData en una cadena con bastante facilidad:

NSData *myData = [self getDataFromSomewhere]; 
NSString *string = [NSString stringWithFormat:@"%.*s", [myData length], [myData bytes]]; 

Si desea dar salida al registro, que puede ser aún más fácil:

NSLog(@"my Data: %.*s", [myData length], [myData bytes]); 
+0

¿Estás seguro del ejemplo de NSLog? No he tenido mucha suerte con el parámetro de longitud, pero NSLog (@ "my Data:% * s", [myData bytes]); funciona un placer ¡Gracias por eso! –

+0

funciona para mí, gracias! –

+0

muy bueno. ¿Alguna posibilidad de que pueda explicar lo que está sucediendo con la declaración de registro? –

20

¿Qué tal

NSString *content = [[[NSString alloc] initWithData:myData 
              encoding:NSUTF8StringEncoding] autorelease]; 
+0

Funciona, gracias. –

1

utilizar una categoría en NSData:

NSData + NSString.h

@interface NSData (NSString) 

- (NSString *)toString; 

@end 

NSData + NSString.m

#import "NSData+NSString.h" 

@implementation NSData (NSString) 

- (NSString *)toString 
{ 
    Byte *dataPointer = (Byte *)[self bytes]; 
    NSMutableString *result = [NSMutableString stringWithCapacity:0]; 
    NSUInteger index; 
    for (index = 0; index < [self length]; index++) 
    { 
     [result appendFormat:@"0x%02x,", dataPointer[index]]; 
    } 
    return result; 
} 

@end 

A continuación, sólo NSLog(@"Data is %@", [nsData toString])"

+0

Oye .. ¿Cómo recuperar NSData de su método de categoría? – CoderSaru

0

A veces es necesario crear base 64 de la cadena codificada NSData. Por ejemplo, cuando crea un correo electrónico MIME. En este caso, use lo siguiente:

#import "NSData+Base64.h" 
NSString *string = [data base64EncodedString]; 
0

Creo que debería probar este código porque lo he usado y funcionará.

NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
2

Puede intentarlo. Es bien conmigo

DLog(@"responeData: %@", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);