Это одна из тех вещей, когда Какао делает все грязное за кулисами, и вы никогда не поймете, насколько сложными могут быть вещи, пока вам не придется засучить рукава и сделать это самостоятельно.
Простой ответ, почему он не «прост», заключается в том, что NSString
(и CFString
) имеют дело со всеми сложными деталями работы с несколькими наборами символов, Unicode и т. Д., И т. Д., В то же время представляя простой унифицированный API для манипулирования строками. Это объектно-ориентированный в лучшем виде - подробности того, «как» (NS|CF)String
имеет дело со строками, которые имеют разные кодировки (UTF8, MacRoman, UTF16, ISO 2022 японский и т. Д.), Являются частной реализацией. Все это «просто работает».
Это помогает понять, как работает [@"..." UTF8String]
. Это частная реализация, поэтому это не Евангелие, а основанное на наблюдаемом поведении. Когда вы отправляете в строку сообщение UTF8String
, строка выполняет что-то приближенное (на самом деле это не проверено, поэтому рассмотрите это как псевдокод, и на самом деле есть более простые способы сделать то же самое, так что это слишком многословно):
- (const char *)UTF8String
{
NSUInteger utf8Length = [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSMutableData *utf8Data = [NSMutableData dataWithLength:utf8Length + 1UL];
char *utf8Bytes = [utf8Data mutableBytes];
[self getBytes:utf8Bytes
maxLength:utf8Length
usedLength:NULL
encoding:NSUTF8StringEncoding
options:0UL
range:NSMakeRange(0UL, [self length])
remainingRange:NULL];
return(utf8Bytes);
}
Вам не нужно беспокоиться о проблемах управления памятью при работе с буфером, который -UTF8String
возвращает, поскольку NSMutableData
автоматически освобожден.
Строковый объект может хранить содержимое строки в любой форме, которую хочет, поэтому нет гарантии, что его внутреннее представление будет наиболее удобным для ваших нужд (в данном случае, UTF8). Если вы используете просто C, вам придется иметь дело с управлением некоторой памятью для хранения любых преобразований строк, которые могут потребоваться. То, что когда-то было простым вызовом -UTF8String
метода, теперь намного, намного сложнее.
Большая часть NSString
фактически реализована в / с CoreFoundation / CFString
, поэтому, очевидно, есть путь от CFStringRef
-> -UTF8String
. Это не так аккуратно и просто, как NSString
-UTF8String
. Большая часть осложнений связана с управлением памятью. Вот как я справлялся с этим в прошлом:
void someFunction(void) {
CFStringRef cfString; // Assumes 'cfString' points to a (NS|CF)String.
const char *useUTF8StringPtr = NULL;
UInt8 *freeUTF8StringPtr = NULL;
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
if((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) {
if((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
}
long utf8Length = (long)((freeUTF8StringPtr != NULL) ? usedBytes : stringLength);
if(useUTF8StringPtr != NULL) {
// useUTF8StringPtr points to a NULL terminated UTF8 encoded string.
// utf8Length contains the length of the UTF8 string.
// ... do something with useUTF8StringPtr ...
}
if(freeUTF8StringPtr != NULL) { free(freeUTF8StringPtr); freeUTF8StringPtr = NULL; }
}
ПРИМЕЧАНИЕ : я не тестировал этот код, но он изменен с рабочего кода. Так что, кроме очевидных ошибок, я считаю, что это должно сработать.
Вышеприведенное пытается получить указатель на буфер, который CFString
использует для хранения содержимого строки. Если в CFString
содержится строковое содержимое, закодированное в UTF8 (или в подходяще совместимой кодировке, такой как ASCII), то, скорее всего, CFStringGetCStringPtr()
вернет не- NULL
. Это, очевидно, лучший и самый быстрый случай. Если он по какой-то причине не может получить этот указатель, скажем, если его содержимое закодировано в CFString
в UTF16, то он выделяет буфер с malloc()
, который достаточно большой, чтобы содержать всю строку, когда он транскодируется в UTF8. Затем в конце функции он проверяет, была ли выделена память, и free()
, если необходимо.
А теперь несколько советов и хитростей ... CFString
«стремится» (и это частная деталь реализации, поэтому он может меняться и меняется между выпусками), сохраняя «простые» строки в кодировке MacRoman, что 8-битная кодировка. MacRoman, как и UTF8, является надмножеством ASCII, так что все символы <128 эквивалентны их аналогам ASCII (или, другими словами, любой символ <128 является ASCII). В MacRoman символы> = 128 являются «специальными» символами. Все они имеют Unicode-эквиваленты и, как правило, представляют собой дополнительные символы валюты и расширенные западные символы. См. Википедия - MacRoman для получения дополнительной информации. Но то, что CFString
говорит, что это MacRoman (CFString
значение кодирования kCFStringEncodingMacRoman
, NSString
значение кодирования NSMacOSRomanStringEncoding
) не означает, что в нем есть символы> = 128. Если строка в кодировке kCFStringEncodingMacRoman
, возвращаемая CFStringGetCStringPtr()
, полностью состоит из символов <128, то она в точности эквивалентна ее кодированному представлению ASCII (<code>kCFStringEncodingASCII), что также в точности эквивалентно строкам UTF8 (kCFStringEncodingUTF8
) закодированное представление.
В зависимости от ваших требований, вы можете «обойтись», используя kCFStringEncodingMacRoman
вместо kCFStringEncodingUTF8
при звонке CFStringGetCStringPtr()
. Вещи «могут» (возможно) будут быстрее, если вам требуется строгая кодировка UTF8 для ваших строк, но используйте kCFStringEncodingMacRoman
, затем убедитесь, что строка, возвращаемая CFStringGetCStringPtr(string, kCFStringEncodingMacRoman)
, содержит только символы <128. 128 в строке, затем идите по медленному маршруту, <code>malloc() используя буфер для хранения преобразованных результатов. Пример:
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
for(CFIndex idx = 0L; (useUTF8String != NULL) && (useUTF8String[idx] != 0); idx++) {
if(useUTF8String[idx] >= 128) { useUTF8String = NULL; }
}
if((useUTF8String == NULL) && ((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL)) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
Как я уже сказал, вы на самом деле не цените, сколько работы Какао выполняет для вас автоматически, пока вам не придется делать все это самостоятельно. :)