wchar_t и Cocoa
написал Владимир Пузанов - August 6, 2008 – 13:07Пару дней назад мне пришлось интеграчить C++ код в проект на Cocoa. Помимо всех прелестей плюсов, целевая либа еще и использовала wchar_t для хранения строк. По идее, при sizeof(wchar_t)==4 там должен быть UTF32 (NSString внутри оперирует UTF16 через unichar). Конечно в Cocoa есть методы для конвертации кодировок, но, почему то, они не захотели работать ожидаемым образом.
Для начала разберемся с выводом wchar_t* из NSString. Для этого мы будем использовать следующую категорию:
@interface NSString (WideCharOps) - (wchar_t *)getWideChars; @end
Всю основную работу выполнит getCString:maxLength:encoding:, кроме того момента, что в конце не обнулены три байта:
- (wchar_t *)getWideChars { unsigned long len = [self length]; wchar_t *buffer = new wchar_t[len+1]; assert([self getCString:(char*)buffer maxLength:(len+1)*sizeof(wchar_t) encoding:NSUTF32StringEncoding]); buffer[len] = L'\0'; return buffer; }
их приходится явно нуллить через buffer[len] = L’\0′.
Как видите, конверсия в эту сторону работает крайне просто и без проблем, при этом имея всего один подводный камень.
К сожалению ввод UTF32 строки в NSString мне сходу не удался. Перепробовав все мыслимые и немыслимые методы (и даже убедив GCиспользовать короткий wchar_t через -fshort-wchar), я пришел к выводу, что придется разбирать строку самому. К счастью вам не придется лезть в дебри кодировок, так как на сайте уникода мною был найден замечательный исходник: ConvertUTF.c. Забираем его и парный хедер из http://www.unicode.org/Public/PROGRAMS/CVTUTF/ и добавляем в проект. Теперь Можно расширить нашу категорию методом
+ (NSString *)stringWithWideChars:(const wchar_t *)src;
который реализовывается следующим образом:
+ (NSString *)stringWithWideChars:(const wchar_t *)src { size_t len = wcslen(src); unichar *dst = (unichar*)malloc(len*sizeof(unichar)+1); const char *src_end = (char*) (src+len+1); unichar *dst_end = dst+len+1; unichar *dst_start = dst; ConversionResult res; ConversionFlags flags=strictConversion; res = ConvertUTF32toUTF16((const UTF32**)&src, (const UTF32*)src_end, (UTF16**)&dst, (UTF16*)dst_end, flags); assert(res == conversionOK); NSString *n = [NSString stringWithCharacters:dst_start length:len]; free(dst_start); return n; }
Все заморочки связаны с выделением временного буфера dst, в котором мы производим конверсию UTF32 -> UTF16. А UTF16 это уже валидная строка для stringWithCharacters:length:.
Вот таким нехитрым образом можно производить интеграцию кода на wchar_t* в проекты Cocoa.