web-dev-qa-db-fra.com

Comment convertir une valeur unichar en NSString dans Objective-C?

J'ai un caractère international stocké dans une variable unichar. Ce caractère ne provient pas d'un fichier ou d'une URL. La variable elle-même ne stocke qu'un court non signé (0xce91) qui est au format UTF-8 et se traduit par la lettre majuscule grecque 'A'. J'essaie de mettre ce caractère dans une variable NSString mais j'échoue lamentablement.

J'ai essayé 2 façons différentes qui ont échoué:

unichar greekAlpha = 0xce91; //could have written greekAlpha = 'Α' instead.

NSString *theString = [NSString stringWithFormat:@"Greek Alpha: %C", greekAlpha];

Pas bien. Je reçois des caractères chinois étranges. En tant que sidenote, cela fonctionne parfaitement avec les caractères anglais.

Ensuite, j'ai également essayé ceci:

NSString *byteString = [[NSString alloc] initWithBytes:&greekAlpha
                                                length:sizeof(unichar)
                                              encoding:NSUTF8StringEncoding];

Mais cela ne fonctionne pas non plus. Je fais évidemment quelque chose de terriblement mal, mais je ne sais pas quoi. Quelqu'un peut-il m'aider s'il vous plaît? Merci!

31
Terry

Puisque 0xce91 est au format UTF-8 et %C s'attend à ce qu'il soit en UTF-16, une solution simple comme celle ci-dessus ne fonctionnera pas. Pour stringWithFormat:@"%C" pour travailler, vous devez saisir 0x391 qui est l'unicode UTF-16.

Pour créer une chaîne à partir de l'unichar encodé UTF-8, vous devez d'abord diviser l'unicode en ses octets, puis utiliser initWithBytes:length:encoding.

unichar utf8char = 0xce91; 
char chars[2];
int len = 1;

if (utf8char > 127) {
    chars[0] = (utf8char >> 8) & (1 << 8) - 1;
    chars[1] = utf8char & (1 << 8) - 1; 
    len = 2;
} else {
    chars[0] = utf8char;
}

NSString *string = [[NSString alloc] initWithBytes:chars
                                            length:len 
                                          encoding:NSUTF8StringEncoding];
21
hallski
unichar greekAlpha = 0x0391;
NSString* s = [NSString stringWithCharacters:&greekAlpha length:1];

Et maintenant, vous pouvez incorporer cette chaîne NSString dans une autre de la manière qui vous convient. Notez cependant qu'il est désormais légal de taper un alpha grec directement dans un littéral NSString.

55
matt

La réponse ci-dessus est excellente mais ne tient pas compte des caractères UTF-8 de plus de 16 bits, par ex. le symbole Ellipsis - 0xE2,0x80,0xA6. Voici un Tweak au code:

if (utf8char > 65535) {
   chars[0] = (utf8char >> 16) & 255;
   chars[1] = (utf8char >> 8) & 255;
   chars[2] = utf8char & 255; 
   chars[3] = 0x00;
} else if (utf8char > 127) {
    chars[0] = (utf8char >> 8) & 255;
    chars[1] = utf8char & 255; 
    chars[2] = 0x00;
} else {
    chars[0] = utf8char;
    chars[1] = 0x00;
}
NSString *string = [[[NSString alloc] initWithUTF8String:chars] autorelease];

Notez la méthode d'initialisation de chaîne différente qui ne nécessite pas de paramètre de longueur.

2
Jon Jardine

Voici un algorithme pour le codage UTF-8 sur un seul caractère:

if (utf8char<0x80){ 
    chars[0] = (utf8char>>0)  & (0x7F | 0x00);
    chars[1] = 0x00;
    chars[2] = 0x00;
    chars[3] = 0x00;
}
else if (utf8char<0x0800){
    chars[0] = (utf8char>>6)  & (0x1F | 0xC0);
    chars[1] = (utf8char>>0)  & (0x3F | 0x80);
    chars[2] = 0x00;
    chars[3] = 0x00;
}
else if (utf8char<0x010000) {
    chars[0] = (utf8char>>12) & (0x0F | 0xE0);
    chars[1] = (utf8char>>6)  & (0x3F | 0x80);
    chars[2] = (utf8char>>0)  & (0x3F | 0x80);
    chars[3] = 0x00;
}
else if (utf8char<0x110000) {
    chars[0] = (utf8char>>18) & (0x07 | 0xF0);
    chars[1] = (utf8char>>12) & (0x3F | 0x80);
    chars[2] = (utf8char>>6)  & (0x3F | 0x80);
    chars[3] = (utf8char>>0)  & (0x3F | 0x80);
}
1
yusufag

Le code ci-dessus est l'équivalent moral de unichar foo = 'abc';.

Le problème est que 'Α' ne correspond pas à un seul octet dans le "jeu de caractères d'exécution" (je suppose UTF-8) qui est "défini par l'implémentation" dans C99 §6.4.4.4 10:

La valeur d'une constante de caractère entier contenant plusieurs caractères (par exemple, 'ab'), ou contenant un caractère ou une séquence d'échappement qui ne correspond pas à un caractère d'exécution à un octet, est défini par l'implémentation.

Une façon consiste à faire 'ab' égal à 'a'<<8|b. Certains en-têtes de système Mac/iOS en dépendent pour des choses comme OSType/FourCharCode/ FourCC ; le seul dans iOS qui me vient à l'esprit est les formats de pixels CoreVideo. Ceci n'est cependant pas transférable.

Si vous voulez vraiment un unichar littéral, vous pouvez essayer L'A' (techniquement c'est un wchar_t littéral, mais sous OS X et iOS, wchar_t est généralement UTF-16, donc cela fonctionnera pour les choses à l'intérieur du BMP). Cependant, il est beaucoup plus simple d'utiliser simplement @"Α" (qui fonctionne tant que vous définissez correctement le codage des caractères source) ou @"\u0391" (qui fonctionne depuis au moins le SDK iOS 3).

1
tc.