Подтвердить что ты не робот

Преобразовать байты 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 больше вероятно, будет виноват.

4b9b3361

Ответ 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];