NSLogv C строки кодирования - PullRequest
0 голосов
/ 01 октября 2019

Я написал небольшую оболочку для ведения журнала, используя NSLogv:

void MyLog(const char* format, ...) {
    va_list vargs;
    va_start(vargs, format);
    NSString* formatStr = [NSString stringWithUTF8String:format];
    NSLogv(formatStr, vargs);
    va_end(vargs);
}

, которую я могу использовать следующим образом:

MyLog("%d - %s", 123, "ABCD");

Проблема у меня возникает, когда я использую символы внестандартный диапазон ASCII:

MyLog("%d - %s", 123, "АБВГ");

NSLogv не может правильно кодировать эти символы:

2019-10-01 11:10:30.890346+0300 TestApp[86349:7051788] 123 - –ê–ë–í–ì

Каков будет правильный способ кодирования таких символов при сохранении вариабельной подписи моего помощникаmethod?

PS Пробовал как на X86_64 симуляторе, так и на ARM64 устройстве

Если я преобразую строки C в UTF16, то он будет работать как положено:

std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
std::u16string value16 = convert.from_bytes("АБВГ");
MyLog("%d - %S", 123, value16.c_str());

Ответы [ 2 ]

2 голосов
/ 02 октября 2019

Вы обнаружили любопытную «особенность» форматирования в Objective-C и должны посетить Помощник Apple по обратной связи и сообщить об этом.

Так что же происходит? Ну, это вообще не имеет ничего общего с функциями вариации, и не имеет ничего общего с NSLogv как таковым . Скорее это связано с базовым кодом форматирования Objective C, который NSLog, NSLogv, stringWithFormat:, и др. использует и тип самой строки форматирования. ..

Вот простая демонстрация «функции»:

- (void)demo
{
   char *sample = "АБВГ"; // This will be UTF-8 encoded

   // use %p to show address, %s to show string, \n as printf doesn't add one
   char *cFormat = "%p - %s\n";
   NSString *nsFormat = @"%p - %s\n"; // produces an __NSCFConstantString
   NSString *convertedFormat = [NSString stringWithUTF8String:cFormat]; // produces an __NSCFString

   printf(cFormat, sample, sample); // works
   NSLog(convertedFormat, sample, sample); // fails with __NSCFString
   NSLog(nsFormat, sample, sample); // works with __NSCFConstantString

   NSLog(@"formats equal: %s", [convertedFormat isEqualToString:nsFormat] ? "yes" : "no"); // __NSCFString & __NSCFConstantString are equal
}

Запустите это, и консоль покажет что-то вроде:

0x1000013f8 - АБВГ
2019-10-01 10:25:48.222537+0100 demo[8435:1431874] 0x1000013f8 - –ê–ë–í–ì

2019-10-01 10:25:48.222560+0100 demo[8435:1431874] 0x1000013f8 - АБВГ
2019-10-01 10:25:48.222582+0100 demo[8435:1431874] formats equal: yes

Итак, Cбиблиотека printf работает, NSLog с константой NSString работает, но NSLog с NSString форматом, преобразованным из char *, не работает, и все же последние два формата сравниваются одинаково... Обратите также внимание, что в случае ошибки NSLog добавляет дополнительную новую строку.

Плохой вывод –ê–ë–í–ì - это та же интерпретация строки аргумента, которую Xcode показывает для байтов памяти строки. Таким образом, базовый тип строки format определяет, как интерпретируются базовые байты строки аргумента ...

Такая странная «особенность» заставляет задуматьсябыл ли это разработан по какой-то причине, или мы упускаем очевидное ... Может быть, кто-то еще может просветить нас, но если они этого не сделают, давайте назовем это (любопытно) ошибкой!

Обходной путь

Как показывает вышеприведенная демонстрация использования функций формата библиотеки C, так что если вы довольны потерей преамбулы NSLog на каждом наброске, вы можете просто использовать одну из них вваша функция:

void MyLog(const char *format, ...)
{
   va_list vargs;
   va_start(vargs, format);
   vprintf(format, vargs);
   va_end(vargs);
}

Если вы хотите сохранить NSLog для вывода, вы можете использовать один из эквивалентов библиотеки C stringWithFormat:, вот версия вашей функции, которая динамически распределяет необходимыеместо для отформатированной строки C, а затем освободите ее (ARC не сделает этого за вас!):

void MyLog(const char *format, ...)
{
   va_list vargs;
   va_start(vargs, format);
   char *output;
   vasprintf(&output, format, vargs);
   NSLog(@"%s", output);
   free(output);
   va_end(vargs);
}

«Функция» все еще существует в последней версии Xcode 11 и macOS Catalina Beta, поэтому, пожалуйста, сделайте этоотправьте сообщение об этом в Помощник Apple по обратной связи .

HTH

1 голос
/ 01 октября 2019

Похоже на ошибку в функции NSLogv, потому что NSLog, printf, vprintf работает нормально. Вместо этого я могу предложить использовать макрос:

#define MyLog(arg, ...) NSLog(@ arg, __VA_ARGS__)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...