Я думаю, что очень важно, чтобы люди понимали, как бороться с юникодом, поэтому я написал ответ монстра, но в духе tl; dr Начну с фрагмента, который должен работать нормально. Если вы хотите узнать подробности (что вам нужно!), Продолжайте читать после фрагмента.
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]);
}
Еще со мной? Хорошо!
Нынешний принятый ответ, похоже, путает байты с символами/буквами. Это обычная проблема при встрече с unicode, особенно на фоне C. Строки в Objective-C представлены как символы Unicode (unichar
), которые намного больше, чем байты, и не должны использоваться со стандартными функциями управления строкой C.
(Edit:. Это не полная история К моему великому стыду, я совершенно забыл объяснить компонуемые персонаж, где «буква» состоит из нескольких кодовых Юникода Это дает вам ситуация, когда у вас может быть одна «буква», разрешающая несколько unichars, которая, в свою очередь, состоит из нескольких байтов. Hoo boy. Подробнее об этом см. на this great answer.)
Правильный ответ на этот вопрос зависит от того, хотите ли вы, чтобы перебрать символов/букв (в отличие от типа char
) или байт строки (что тип char
фактически означает) , В духе ограничения путаницы я буду использовать термины байт и письмо с этого момента, избегая, возможно, двусмысленного термина character.
Если вы хотите сделать первое и перебрать буквы в строке, вам нужно иметь дело исключительно с unichars (извините, но сейчас мы в будущем, вы больше не можете его игнорировать). Найти количество букв легко, это свойство длины строки. Пример сниппет как таковой (то же самое, что и выше):
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]);
}
Если, с другой стороны, вы хотите перебрать байтов в строке, он начинает получать сложный и результат будет полностью зависеть от кодирования вы решили использовать. Достойным выбором по умолчанию является UTF8, так что я покажу.
Для этого вам нужно выяснить, сколько байтов будет полученной строкой UTF8, шаг, где легко ошибиться и использовать строку -length
. Одна из основных причин, почему это очень легко сделать, особенно для американского разработчика, состоит в том, что строка с буквами, попадающими в 7-битный ASCII-спектр, будет иметь равные байты и длины букв. Это связано с тем, что UTF8 кодирует 7-битные буквы ASCII с одним байтом, поэтому простая тестовая строка и основной текст на английском могут работать отлично.
Правильный способ сделать это состоит в использовании метода -lengthOfBytesUsingEncoding:NSUTF8StringEncoding
(или другое кодирования), выделить буфер с той длиной, затем преобразовать строку в ту же кодировку с -cStringUsingEncoding:
и скопировать его в этот буфер. Пример код здесь:
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]);
}
Просто ехать домой точки, почему это важно, чтобы держать вещи прямо, я покажу пример код, который обрабатывает эту итерацию четыре различных способов, два неправильно и два правильных. Это код:
#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;
}
Выполнение этого вывода кода будет следующим (с NSLog хлама обрезается выход), показывая точно, как различные байт и буквенные представления могут быть (два последних выходов):
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: °
Это правильный способ сделать это, но стоит иметь в виду, что любое преобразование символов в NSString вдоль этих строк будет затрагивать несколько очень сложных фреймов с многобайтовым текстом, и его лучше избегать, когда вообще возможное. (И просто использовать UTF-16 или UTF-32, к сожалению, недостаточно, чтобы обойти все проблемы международного текста, хотя он будет снимать ваши требования к памяти на Луну.) – Chuck
@Chuck, Fair point. –
Зачем вам делать буфер символов? – ma11hew28