Конвертировать байты NSData в NSString? - PullRequest
49 голосов
/ 15 февраля 2009

Я пытаюсь использовать класс 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);

.. который извлекает данные, но, похоже, после них появляется дополнительный мусор в Юникоде:

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

Я также пытался ( отсюда ) ..

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

.. но это, кажется, возвращает кучу случайных символов:

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

Тот факт, что первый способ (как упомянуто в документации Apple) возвращает большинство данных правильно, с некоторыми дополнительными байтами, заставляет меня думать, что это может быть ошибкой в ​​библиотеке BEncoding ... но мое отсутствие знаний об ObjC больше скорее всего, виноват ..

Ответы [ 10 ]

99 голосов
/ 03 марта 2009

Это важный момент, который я должен подчеркнуть еще раз. Оказывается,

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

не то же самое, что

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

первый ожидает завершенную байтовую строку NULL, второй нет. В вышеупомянутых двух случаях content будет НЕДЕЙСТВИТЕЛЕН в первом примере, если строка байта не завершена правильно.

20 голосов
/ 10 февраля 2011

Как насчет

NSString *content = [[[NSString alloc] initWithData:myData
                                           encoding:NSUTF8StringEncoding] autorelease];
19 голосов
/ 17 февраля 2009
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];

Когда я NSLog торрент я получаю следующее:

{
    ⋮
}

Тогда это будет NSDictionary, а не NSData.

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

.., который извлекает данные, но, кажется, после них появляется дополнительный мусор в Юникоде:

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:. Это проще, потому что не требует данных с нулевым символом в конце; он просто использует длину, которая у вас уже есть. (Не забудьте выпустить или автоматически выпустить его.)

7 голосов
/ 26 августа 2009

Хороший быстрый и грязный подход - использовать NSString stringWithFormat инициализатор, чтобы помочь вам. Одна из редко используемых функций форматирования строки - это возможность указать максимальную длину строки при выводе строки. Использование этой удобной функции позволяет довольно легко преобразовать NSData в строку:

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

Если вы хотите вывести его в журнал, это может быть еще проще:

NSLog(@"my Data: %.*s", [myData length], [myData bytes]);
6 голосов
/ 15 февраля 2009

Ага, метод 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
2 голосов
/ 05 августа 2013

Вы можете попробовать это. Хорошо со мной.

DLog(@"responeData: %@", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);
2 голосов
/ 02 марта 2012

Использовать категорию на 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])"

2 голосов
/ 16 февраля 2009

В тех случаях, когда у меня нет контроля над данными, преобразуемыми в строку, например при чтении из сети, я предпочитаю использовать NSString -initWithBytes:length:encoding:, чтобы не зависеть от того, будет ли строка с нулевым символом в конце чтобы получить определенные результаты. Обратите внимание, что в документации Apple говорится, что если cString не является строкой с нулевым символом в конце, то результаты не определены.

0 голосов
/ 05 июня 2013

Это будет работать.

NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
0 голосов
/ 05 октября 2012

Иногда вам нужно создать строку в кодировке Base64 из NSData. Например, при создании электронной почты MIME. В этом случае используйте следующее:

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