Creo que es importante que las personas entiendan cómo lidiar con Unicode, así que terminé escribiendo una respuesta monstruosa, pero en el espíritu de tl; dr Comenzaré con un fragmento que debería funcionar bien. Si desea conocer los detalles (¡lo que debería hacer!), Continúe leyendo después del fragmento.
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]);
}
¿Seguirías conmigo? ¡Bueno!
La respuesta aceptada actualmente parece confundir bytes con caracteres/letras. Este es un problema común al encontrar unicode, especialmente desde un fondo C. Las cadenas en Objective-C se representan como caracteres Unicode (unichar
) que son mucho más grandes que los bytes y no deben utilizarse con las funciones de manipulación de cadenas C estándar.
(Editar:!. Esta no es la historia completa Para mi vergüenza, me había olvidado por completo para dar cuenta de caracteres componibles, donde una "carta" se compone de múltiples puntos de código Unicode Esto le da una situación en la que puede tener una "letra" que se resuelve en varios unichars, que a su vez son múltiples bytes cada uno. Hoo boy. Consulte this great answer para obtener detalles sobre eso.)
La respuesta correcta a la pregunta depende de si se desea iterar sobre los caracteres/letras (a diferencia del tipo char
) o la bytes de la cadena (lo que el tipo char
significa en realidad) . Con el ánimo de limitar la confusión, utilizaré los términos byte y , letra a partir de ahora, evitando el término posiblemente ambiguo , carácter.
Si desea hacer lo anterior e iterar sobre las letras de la cadena, debe tratar exclusivamente con unichars (lo siento, pero estamos en el futuro ahora, ya no puede ignorarlo). Encontrar la cantidad de letras es fácil, es la propiedad de la longitud de la cadena. Un fragmento de ejemplo es como tal (igual al anterior):
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, por el contrario, desea iterar sobre los bytes en una cadena, empieza a ser complicado y el resultado dependerá por completo de la codificación eliges usar La opción predeterminada decente es UTF8, así que eso es lo que mostraré.
Al hacer esto, tiene que calcular cuántos bytes será la cadena UTF8 resultante, un paso donde es fácil equivocarse y usar el -length
de la cadena. Una razón principal por la que esto es muy fácil de hacer, especialmente para un desarrollador estadounidense, es que una cadena con letras que caen en el espectro ASCII de 7 bits tendrá bytes iguales y longitud de letra. Esto se debe a que UTF8 codifica letras ASCII de 7 bits con un solo byte, por lo que una cadena de prueba simple y un texto en inglés básico podrían funcionar perfectamente bien.
La manera apropiada de hacer esto es utilizar el método de -lengthOfBytesUsingEncoding:NSUTF8StringEncoding
(u otra codificación), asignar un búfer con esa longitud, entonces convertir la cadena en la misma codificación con -cStringUsingEncoding:
y copiarlo en ese búfer. Código de ejemplo aquí:
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]);
}
Sólo para remachar el clavo en cuanto a por qué es importante mantener las cosas en orden, que mostrará código de ejemplo que se encarga de esta iteración de cuatro maneras diferentes, dos y dos mal correcta. Este es el código:
#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;
}
La ejecución de esta salida de código de voluntad lo siguiente (con NSLog cruft recortó hacia fuera), mostrando exactamente cómo las diferentes representaciones de bytes y de la letra puede ser (las dos últimas salidas):
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: °
Esta es una buena forma de hacerlo, pero vale la pena tener en cuenta que cualquier transformación inteligente de un NSString en estas líneas llegará a algunos casos extremos muy complicados con texto multibyte, y es mejor evitarlo en absoluto posible. (Y solo usar UTF-16 o UTF-32 lamentablemente no es suficiente para resolver todos los problemas del texto internacional, aunque disparará los requisitos de memoria a la luna). – Chuck
@Chuck, Fair point. –
¿por qué harías un búfer de char? – ma11hew28