web-dev-qa-db-fra.com

Convertir les octets NSData en NSString?

J'essaie d'utiliser la classe BEncoding ObjC pour décoder un .torrent fichier.

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

Lorsque je NSLogtorrent j'obtiens ce qui suit:

{
    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;
....

Comment convertir le name en NSString? J'ai essayé..

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);

..qui récupère les données, mais semble avoir des déchets supplémentaires unicode après:

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

J'ai aussi essayé ( d'ici ) ..

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

..mais cela semble renvoyer un tas de caractères aléatoires:

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

Le fait que la première façon (comme mentionné dans la documentation Apple) renvoie correctement la plupart des données, avec quelques octets supplémentaires me fait penser que cela pourrait être une erreur dans la bibliothèque BEncoding .. mais mon manque de connaissances sur ObjC est plus susceptible d'être en faute.

49
dbr
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];

Quand je torrent NSLog, j'obtiens ce qui suit:

{
    ⋮
}

Ce serait donc un NSDictionary, pas un NSData.

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

..qui récupère les données, mais semble avoir des déchets Unicode supplémentaires après:

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

Non, il a très bien récupéré le nom de fichier; vous l'avez simplement imprimé incorrectement. %s Prend une chaîne C, qui se termine par null; les octets d'un objet de données ne se terminent pas par un caractère nul (ce ne sont que des octets, pas nécessairement des caractères dans n'importe quel codage, et 0 - qui est nul en tant que caractère - est un octet parfaitement valide). Vous devez allouer un caractère de plus et définir le dernier du tableau sur 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);

Mais la terminaison nulle des données dans un objet NSData est incorrecte (sauf lorsque vous avez vraiment besoin d'une chaîne C). J'arriverai dans le bon sens dans un instant.

J'ai aussi essayé […] ..

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

..mais cela semble renvoyer des caractères chinois aléatoires:

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

C'est parce que vos octets sont UTF-8, qui encode un caractère dans (généralement) un octet.

unichar est, et stringWithCharacters:length: accepte, UTF-16. Dans ce codage, un caractère fait (généralement) deux octets. (D'où la division par sizeof(unichar): elle divise le nombre d'octets par 2 pour obtenir le nombre de caractères.)

Alors vous avez dit "voici quelques données UTF-16", et il a créé des caractères à partir de tous les deux octets; chaque paire d'octets était censée être deux caractères, pas un, donc vous avez des ordures (qui se sont avérées être principalement des idéogrammes CJK).


Vous avez répondu à votre propre question plutôt bien, sauf que stringWithUTF8String: Est plus simple que stringWithCString:encoding: Pour les chaînes encodées en UTF-8.

Cependant, lorsque vous avez la longueur (comme vous le faites lorsque vous avez un NSData), il est encore plus facile - et plus approprié - d'utiliser initWithBytes:length:encoding:. C'est plus facile car il ne nécessite pas de données à terminaison nulle; il utilise simplement la longueur que vous avez déjà. (N'oubliez pas de le libérer ou de le libérer automatiquement.)

19
Peter Hosey

C'est un point important qui devrait être souligné à nouveau je pense. Il se trouve que,

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

n'est pas la même chose que,

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

le premier attend une chaîne d'octets terminée par NULL, le second ne le fait pas. Dans les deux cas ci-dessus, content sera NULL dans le premier exemple si la chaîne d'octets n'est pas correctement terminée.

99
Alasdair Allan

Que diriez-vous

NSString *content = [[[NSString alloc] initWithData:myData
                                           encoding:NSUTF8StringEncoding] autorelease];
20
user102008

Une bonne approche rapide et sale consiste à utiliser l'initialiseur NSString de stringWithFormat pour vous aider. L'une des fonctionnalités moins utilisées du formatage de chaîne est la possibilité de spécifier une longueur de chaîne maximale lors de la sortie d'une chaîne. L'utilisation de cette fonction pratique vous permet de convertir assez facilement NSData en chaîne:

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

Si vous souhaitez le publier dans le journal, cela peut être encore plus simple:

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

Aha, la méthode NSStringstringWithCString fonctionne correctement:

Avec le bencoding.h/.m fichiers ajoutés à votre projet, l'ensemble .m fichier:

#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;
}

..et la sortie:

ubuntu-8.10-desktop-i386.iso
6
dbr

Dans les cas où je n'ai pas de contrôle sur les données en cours de transformation, comme la lecture sur le réseau, je préfère utiliser NSString -initWithBytes:length:encoding: pour ne pas dépendre d'une chaîne terminée par NULL afin d'obtenir des résultats définis. Notez que la documentation d'Apple indique que si cString n'est pas une chaîne terminée par NULL, les résultats ne sont pas définis.

2
Jay O'Conor

Utilisez une catégorie sur 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

Alors juste NSLog(@"Data is %@", [nsData toString])"

2

Vous pouvez essayer ça. Ça me va.

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

Cela fonctionnera.

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

Parfois, vous devez créer une chaîne codée Base64 à partir de NSData. Par exemple, lorsque vous créez un MIME de messagerie. Dans ce cas, utilisez les éléments suivants:

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