50 votes

Convertir les octets NSData en NSString ?

J'essaie d'utiliser le Classe ObjC BEncoding pour décoder un .torrent fichier.

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

Quand je NSLog torrent 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 dans une 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 qui semble avoir des déchets unicode supplémentaires après lui :

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

J'ai également 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 méthode (mentionnée dans la documentation d'Apple) renvoie la plupart des données correctement, avec quelques octets supplémentaires, me fait penser qu'il pourrait s'agir d'une erreur dans la bibliothèque BEncoding mais mon manque de connaissance d'ObjC est plus probablement en cause

0 votes

"" n'est pas du chinois. Du moins pas ceux qu'un locuteur natif chinois pourrait reconnaître.

0 votes

@TylerLong point juste, édité !

100voto

Alasdair Allan Points 1773

C'est un point important qui devrait être souligné à nouveau, je pense. Il s'avère 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 à terminaison NULL, le second non. 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.

2 votes

+1 Je me faisais tuer par une erreur que je n'arrivais pas à comprendre. Merci pour ce commentaire perspicace. Vous m'avez évité des heures de frustration.

0 votes

Excellent point. J'ai modifié ma propre réponse pour le couvrir ; c'est un détail essentiel, et je m'excuse de l'avoir omis.

1 votes

Votre réponse va droit au but, Monsieur, et ça marche. Merci !

20voto

user102008 Points 8748

Et si

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

19voto

Peter Hosey Points 66275
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];

Lorsque je NSLog torrent j'obtiens ce qui suit :

{
    ⋮
}

Il s'agirait alors d'un NSDictionary, et non d'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 qui semble avoir des déchets unicode supplémentaires après lui :

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

Non, il a bien récupéré le nom du fichier ; vous l'avez simplement mal imprimé. %s prend une chaîne C, qui est terminée par un caractère nul ; les octets d'un objet de données ne sont pas terminés par un caractère nul (ce ne sont que des octets, pas nécessairement des caractères, quel que soit l'encodage, et 0 - qui est nul en tant que caractère - est un octet parfaitement valide). Vous devriez allouer un caractère supplémentaire et attribuer la valeur 0 au dernier caractère du tableau :

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 n'est pas correcte (sauf lorsque vous vraiment besoin d'une chaîne C). J'aborderai la bonne méthode 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 des UTF-8, qui codent un caractère dans (généralement) un octet.

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

Vous avez donc dit "voici des données UTF-16", et il a créé des caractères tous les deux octets ; chaque paire d'octets était censée contenir deux caractères, et non un, et vous avez donc obtenu des déchets (qui se sont avérés être principalement des idéogrammes CJK).


Vous avez répondu à votre propre question assez bien, sauf que stringWithUTF8String: est plus simple que stringWithCString:encoding: pour les chaînes de caractères codées en UTF-8.

Cependant, lorsque vous disposez de la longueur (comme c'est le cas avec une NSData), il est encore plus facile - et plus correct - d'utiliser la méthode suivante initWithBytes:length:encoding: . Elle est plus simple car elle ne nécessite pas de données à terminaison nulle ; elle utilise simplement la longueur dont vous disposez déjà. (N'oubliez pas de la libérer ou de la libérer automatiquement).

0 votes

StringWithBytes:length:encoding : devrait être initWithBytes:length:encoding : non ?

0 votes

@Brad Cupit : Yup. Désolé pour ça, j'oublie toujours qu'il n'y a pas d'obligation d'achat. stringWith… (car pourquoi n'y en a-t-il pas ? !).

7voto

Ethan Points 296

Une approche rapide et simple consiste à utiliser NSString 's stringWithFormat pour vous aider. L'une des fonctions les moins utilisées du formatage des chaînes de caractères est la possibilité de spécifier une longueur maximale lors de la sortie d'une chaîne de caractères. L'utilisation de cette fonctionnalité pratique vous permet de convertir NSData dans une chaîne de caractères assez facilement :

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

Si vous voulez l'envoyer dans le journal, c'est encore plus facile :

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

0 votes

Etes-vous sûr de l'exemple NSLog ? Je n'ai pas eu beaucoup de chance avec le paramètre de longueur, mais NSLog(@"my Data : %*s", [myData bytes]) ; fonctionne très bien. Merci pour cet exemple !

0 votes

Très cool. Pourriez-vous m'expliquer ce qui se passe avec la déclaration de journal ?

0 votes

L'astérisque vous permet d'insérer un paramètre là où vous auriez normalement une constante dans le format chaîne. Ce paramètre apparaît avant le paramètre normal. Dans ce cas, l'astérisque est remplacé par la longueur des données, et le "s" reçoit le tableau d'octets. Si le tableau avait une longueur de 123 octets, la chaîne équivalente serait NSLog(@"mes données : %.123s", [mesdonnées octets]) ; ce qui produirait les 123 premiers octets de mesdonnées sous forme de chaîne.

6voto

dbr Points 66401

Aha, le NSString méthode stringWithCString fonctionne correctement :

Avec le bencoding.h/.m ajoutés à votre projet, l'ensemble des .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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X