objective c - Convierte bytes NSData a NSString?
objective-c cocoa (10)
Estoy tratando de usar la clase BEncoding ObjC para decodificar un archivo .torrent
.
NSData *rawdata = [NSData dataWithContentsOfFile:@"/path/to/the.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
Cuando realizo NSLog
torrent
obtengo lo 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? Yo 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);
..que recupera los datos, pero parece tener basura Unicode adicional después de ella:
File name: ubuntu-8.10-desktop-i386.iso)
También lo intenté ( desde aquí ) ...
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 es más probable que mi desconocimiento sobre ObjC sea en falta..
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
Cuando realizo NSLog torrent obtengo lo siguiente:
{ ⋮ }
Eso sería un NSDictionary, entonces, no un NSData.
unsigned char aBuffer[[name length]]; [name getBytes:aBuffer length:[name length]]; NSLog(@"File name: %s", aBuffer);
..que recupera los datos, pero parece tener basura Unicode adicional después de ella:
File name: ubuntu-8.10-desktop-i386.iso)
No, recuperó el nombre del archivo muy bien; simplemente lo imprimiste incorrectamente. %s
toma una cadena C, que termina en nulo; 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). Tendría que asignar un carácter más y establecer el último en la matriz en 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 la terminación nula de los datos en un objeto NSData es incorrecta (excepto cuando realmente se necesita una cadena C). 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 devolver caracteres chinos al azar:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
Esto se debe a que sus bytes son UTF-8, que codifica un carácter en (normalmente) 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).
Entonces dijiste "aquí hay algunos datos UTF-16", y creó 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).
Usted contestó su propia pregunta bastante bien, excepto que stringWithUTF8String:
es más simple que stringWithCString:encoding:
para cadenas stringWithCString:encoding:
en UTF-8.
Sin embargo, cuando tiene la longitud (como lo hace cuando tiene un NSData), es incluso más fácil, y más adecuado, utilizar 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).
Aha, el método stringWithCString
funciona correctamente:
Con los archivos bencoding.h/.m
agregados a su proyecto, el archivo .m
completo:
#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 la salida:
ubuntu-8.10-desktop-i386.iso
Algunas veces necesita crear cadenas codificadas en Base64 desde NSData. Por ejemplo, cuando crea un correo electrónico MIME. En este caso, use lo siguiente:
#import "NSData+Base64.h"
NSString *string = [data base64EncodedString];
Creo que deberías probar este código porque lo he usado y funcionará.
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
En los casos en que no tengo control sobre los datos que se transforman en una cadena, como leer desde la red, prefiero usar NSString -initWithBytes:length:encoding:
para que no dependa de tener una cadena terminada NULL 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.
Ese es un punto importante que debería volver a enfatizarse, creo. Resulta que,
NSString *content = [NSString stringWithUTF8String:[responseData bytes]];
no es lo mismo que
NSString *content = [[NSString alloc] initWithBytes:[responseData bytes]
length:[responseData length] encoding: NSUTF8StringEncoding];
el primero espera una cadena de bytes terminados NULL, el segundo no. En los dos casos anteriores, el content
será NULL en el primer ejemplo si la cadena de bytes no termina correctamente.
Puedes probar esto. Está bien conmigo
DLog(@"responeData: %@", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);
Qué tal si
NSString *content = [[[NSString alloc] initWithData:myData
encoding:NSUTF8StringEncoding] autorelease];
Un buen enfoque rápido y sucio es usar el inicializador stringWithFormat
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 función te permite convertir NSData
en una cadena con bastante facilidad:
NSData *myData = [self getDataFromSomewhere];
NSString *string = [NSString stringWithFormat:@"%.*s", [myData length], [myData bytes]];
Si desea enviarlo al registro, puede ser incluso más fácil:
NSLog(@"my Data: %.*s", [myData length], [myData bytes]);
Use 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
Entonces simplemente NSLog(@"Data is %@", [nsData toString])"