Преобразовать байты NSData в NSString?
Я пытаюсь использовать класс BEncoding ObjC для декодирования файла .torrent
.
NSData *rawdata = [NSData dataWithContentsOfFile:@"/path/to/the.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
Когда я NSLog
torrent
, я получаю следующее:
{
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;
....
Как преобразовать name
в NSString? Я пробовал..
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);
.., который извлекает данные, но после него появляется дополнительный мусор Unicode:
File name: ubuntu-8.10-desktop-i386.iso)
Я также пробовал (здесь)..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
.. но это, кажется, возвращает кучу случайных символов:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
Тот факт, что первый способ (как упоминалось в документации Apple) возвращает большую часть данных правильно, с некоторыми дополнительными байтами заставляет меня думать, что это может быть ошибка в библиотеке BEncoding.., но моя нехватка знаний об ObjC больше вероятно, будет виноват.
Ответы
Ответ 1
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
Когда я торгую NSLog, я получаю следующее:
{
⋮
}
Это будет NSDictionary, а не NSData.
unsigned char aBuffer[[name length]];
[name getBytes:aBuffer length:[name length]];
NSLog(@"File name: %s", aBuffer);
.., который извлекает данные, но после него появляется дополнительный мусор Unicode:
File name: ubuntu-8.10-desktop-i386.iso)
Нет, он получил имя файла просто отлично; вы просто напечатали его неправильно. %s
принимает строку C, которая заканчивается нулем; байты объекта данных не заканчиваются на нуль (это просто байты, не обязательно символы в любой кодировке, а 0, который является нулевым как символ, - это абсолютно допустимый байт). Вам нужно было бы выделить еще один символ и установить последнее в массиве в 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);
Но нулевое завершение данных в объекте NSData неверно (за исключением случаев, когда вам действительно нужна строка C). Я вернусь в нужный момент.
Я также пробовал [...]..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
.. но это, кажется, возвращает случайных китайских символов:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
Это потому, что ваши байты UTF-8, который кодирует один символ в (обычно) один байт.
unichar
есть, и stringWithCharacters:length:
принимает, UTF-16. В этой кодировке один символ (обычно) состоит из двух байтов. (Следовательно, деление на sizeof(unichar)
: оно делит количество байтов на 2, чтобы получить количество символов.)
Итак, вы сказали "вот некоторые данные UTF-16", и он пошел и сделал символы из каждых двух байтов; каждая пара байтов должна была быть двумя символами, а не одной, поэтому вы получили мусор (который, как оказалось, был главным образом идеографами CJK).
Вы хорошо ответили на свой вопрос, за исключением того, что stringWithUTF8String:
проще, чем stringWithCString:encoding:
для строк с кодировкой UTF-8.
Однако, когда у вас есть длина (как и при NSData), это еще проще и правильнее использовать initWithBytes:length:encoding:
. Это проще, потому что оно не требует данных с нулевым завершением; он просто использует длину, которую вы уже имеете. (Не забудьте выпустить или автообновить его.)
Ответ 2
Это важный момент, который следует переосмыслить, я думаю. Оказывается, что
NSString *content = [NSString stringWithUTF8String:[responseData bytes]];
не совпадает с
NSString *content = [[NSString alloc] initWithBytes:[responseData bytes]
length:[responseData length] encoding: NSUTF8StringEncoding];
первый ожидает NULL завершаемой байтовой строки, второй - нет. В приведенных выше двух случаях content
будет NULL в первом примере, если строка байта не будет правильно завершена.
Ответ 3
Как насчет
NSString *content = [[[NSString alloc] initWithData:myData
encoding:NSUTF8StringEncoding] autorelease];
Ответ 4
Хорошим быстрым и грязным подходом является использование инициализатора NSString
stringWithFormat
, чтобы помочь вам. Одной из наиболее часто используемых функций форматирования строк является возможность указать длину строки mximum при выводе строки. Используя эту удобную функцию, вы можете легко конвертировать NSData
в строку:
NSData *myData = [self getDataFromSomewhere];
NSString *string = [NSString stringWithFormat:@"%.*s", [myData length], [myData bytes]];
Если вы хотите вывести его в журнал, это может быть еще проще:
NSLog(@"my Data: %.*s", [myData length], [myData bytes]);
Ответ 5
Aha, метод NSString
stringWithCString
работает правильно:
При добавлении файлов bencoding.h/.m
к вашему проекту полный файл .m
:
#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;
}
.. и вывод:
ubuntu-8.10-desktop-i386.iso
Ответ 6
В тех случаях, когда я не контролирую данные, которые преобразуются в строку, например, чтение из сети, я предпочитаю использовать NSString -initWithBytes:length:encoding:
, чтобы я не зависел от того, что строка с нулевым завершением была упорядочена для получения определенных результатов. Обратите внимание, что в документации Apple говорится, что если cString не является строкой с завершающим символом NULL, результаты undefined.
Ответ 7
Используйте категорию в 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
Тогда просто NSLog(@"Data is %@", [nsData toString])"
Ответ 8
Вы можете попробовать это. Я согласен
DLog(@"responeData: %@", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);
Ответ 9
Иногда вам нужно создать строку с кодировкой Base64 из NSData. Например, когда вы создаете MIME для электронной почты. В этом случае используйте следующее:
#import "NSData+Base64.h"
NSString *string = [data base64EncodedString];
Ответ 10
Это будет работать
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];