Je pense qu'il est important que les gens comprennent la façon de traiter avec unicode, donc j'ai fini par écrire un monstre de réponse, mais dans l'esprit de tl;dr je vais commencer avec un morceau de code qui devrait fonctionner correctement. Si vous voulez connaître les détails (que vous devriez!), s'il vous plaît continuer la lecture après le fragment de code.
NSUInteger len = [str length];
unichar buffer[len+1];
[str getCharacters:buffer range:NSMakeRange(0, len)];
NSLog(@"getCharacters:range: with unichar buffer");
for(int i = 0; i < len; i++) {
NSLog(@"%C", buffer[i]);
}
Toujours avec moi? Bon!
L'actuel accepté la réponse semble être source de confusion octets avec des personnages et des lettres. C'est un problème courant lors de la rencontre d'unicode, en particulier à partir d'un C en arrière-plan. Les chaînes en Objective-C sont représentés comme des caractères unicode (unichar
) qui sont beaucoup plus gros que les octets et ne doit pas être utilisé avec la norme C fonctions de manipulation de chaîne.
La bonne réponse à la question dépend si vous souhaitez effectuer une itération sur les caractères et les lettres (comme distinct du type char
) ou les octets de la chaîne (de quel type char
signifie en fait). Dans l'esprit de la limitation de la confusion, je vais utiliser les termes d'octets et de la lettre à partir de maintenant, en évitant la peut-être ambiguë en terme de caractère.
Si vous voulez faire de l'ancien et itérer sur les lettres dans la chaîne, vous devez exclusivement avec unichars (désolé, mais nous sommes dans le futur maintenant, vous ne pouvez pas l'ignorer plus). Trouver la quantité de lettres, c'est facile, c'est la longueur de la chaîne de propriété. Un exemple extrait est en tant que tel (le même que ci-dessus):
NSUInteger len = [str length];
unichar buffer[len+1];
[str getCharacters:buffer range:NSMakeRange(0, len)];
NSLog(@"getCharacters:range: with unichar buffer");
for(int i = 0; i < len; i++) {
NSLog(@"%C", buffer[i]);
}
Si, d'autre part, vous souhaitez effectuer une itération sur les octets en une chaîne de caractères, ça commence à devenir compliqué et le résultat dépend de l'encodage vous choisissez d'utiliser. L'décent choix par défaut est UTF-8, donc c'est ce que je vais montrer.
Ce faisant, vous devez déterminer combien d'octets sont la résultante chaîne UTF8 sera, une étape où il est facile de se tromper et d'utiliser la chaîne de caractères -length
. L'une des principales raisons de cette très facile à faire mal, surtout pour un développeur, c'est qu'une chaîne de caractères avec des lettres de tomber dans l'ASCII 7 bits spectre sera égal octet et lettre de longueurs. C'est parce que UTF8 code ASCII 7 bits lettres avec un seul octet, donc une simple chaîne de test et un anglais de base, le texte pourrait fonctionner parfaitement bien.
La bonne façon de le faire est d'utiliser la méthode de -lengthOfBytesUsingEncoding:NSUTF8StringEncoding
(ou autre encodage), allouer un tampon avec cette longueur, puis convertir la chaîne de la même encodage avec -cStringUsingEncoding:
et le copier dans la mémoire tampon. Exemple de code ici:
NSUInteger byteLength = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
char proper_c_buffer[byteLength+1];
strncpy(proper_c_buffer, [str cStringUsingEncoding:NSUTF8StringEncoding], byteLength);
NSLog(@"strncpy with proper length");
for(int i = 0; i < byteLength; i++) {
NSLog(@"%c", proper_c_buffer[i]);
}
Juste pour enfoncer le clou quant à pourquoi il est important de garder les choses claires, je vais montrer un exemple de code qui gère cette itération de quatre façons différentes, deux de mal et les deux corrects. C'est le code:
#import <Foundation/Foundation.h>
int main() {
NSString *str = @"буква";
NSUInteger len = [str length];
// Try to store unicode letters in a char array. This will fail horribly
// because getCharacters:range: takes a unichar array and will probably
// overflow or do other terrible things. (the compiler will warn you here,
// but warnings get ignored)
char c_buffer[len+1];
[str getCharacters:c_buffer range:NSMakeRange(0, len)];
NSLog(@"getCharacters:range: with char buffer");
for(int i = 0; i < len; i++) {
NSLog(@"Byte %d: %c", i, c_buffer[i]);
}
// Copy the UTF string into a char array, but use the amount of letters
// as the buffer size, which will truncate many non-ASCII strings.
strncpy(c_buffer, [str UTF8String], len);
NSLog(@"strncpy with UTF8String");
for(int i = 0; i < len; i++) {
NSLog(@"Byte %d: %c", i, c_buffer[i]);
}
// Do It Right (tm) for accessing letters by making a unichar buffer with
// the proper letter length
unichar buffer[len+1];
[str getCharacters:buffer range:NSMakeRange(0, len)];
NSLog(@"getCharacters:range: with unichar buffer");
for(int i = 0; i < len; i++) {
NSLog(@"Letter %d: %C", i, buffer[i]);
}
// Do It Right (tm) for accessing bytes, by using the proper
// encoding-handling methods
NSUInteger byteLength = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
char proper_c_buffer[byteLength+1];
const char *utf8_buffer = [str cStringUsingEncoding:NSUTF8StringEncoding];
// We copy here because the documentation tells us the string can disappear
// under us and we should copy it. Just to be safe
strncpy(proper_c_buffer, utf8_buffer, byteLength);
NSLog(@"strncpy with proper length");
for(int i = 0; i < byteLength; i++) {
NSLog(@"Byte %d: %c", i, proper_c_buffer[i]);
}
return 0;
}
L'exécution de ce code de sortie de celui-ci (avec NSLog trucs garni out), montrant exactement COMMENT les différents octets et lettre de représentations (les deux dernières sorties):
getCharacters:range: with char buffer
Byte 0: 1
Byte 1:
Byte 2: C
Byte 3:
Byte 4: :
strncpy with UTF8String
Byte 0: Ð
Byte 1: ±
Byte 2: Ñ
Byte 3:
Byte 4: Ð
getCharacters:range: with unichar buffer
Letter 0: б
Letter 1: у
Letter 2: к
Letter 3: в
Letter 4: а
strncpy with proper length
Byte 0: Ð
Byte 1: ±
Byte 2: Ñ
Byte 3:
Byte 4: Ð
Byte 5: º
Byte 6: Ð
Byte 7: ²
Byte 8: Ð
Byte 9: °