Какая разница между printf ( "% s" ), printf ( "% ls" ), wprintf ( "% s" ) и wprintf ( "% ls" )?

Рассмотрим эту примерную программу:

#include <cstdio>
#include <cwchar>
#include <string>

int main()
{
    std::string narrowstr = "narrow";
    std::wstring widestr = L"wide";
    printf("1 %s \n", narrowstr.c_str());
    printf("2 %ls \n", widestr.c_str());
    wprintf(L"3 %s \n", narrowstr.c_str());
    wprintf(L"4 %ls \n", widestr.c_str());

   return 0;
}

Результат этого:

1 narrow 
2 wide 

Мне интересно:

  • почему 3 и 4 не печатали
  • какие различия между 1 и 3 и 2 и 4.
  • Не имеет значения, если узкий str находится в utf8, а widestr - в utf16?

Ответ 1

Вам нужно сделать:

wprintf(L"3 %hs \n", narrowstr.c_str());
wprintf(L"4 %s \n", widestr.c_str());

Почему? Потому что для printf, % s говорит узкий char -string. Для wprintf, % ls говорит широко.

Но для wprintf % s подразумевается широкий, % ls будет означать широту. % hs будет означать узкий (для обоих). Для printf, % s, таким образом будет просто означать % hs

В VС++/Windows, %S (capital S), будет отменено действие. Для printf("%S") это будет означать широкое, а wprintf("%S") будет означать узкий. Это полезно для _tprintf.

Ответ 2

Обратите внимание, что вы используете потоки C. C-потоки имеют очень особое качество, называемое "ориентация". Поток является либо неориентированным, либо широким, либо узким. Ориентация определяется первым выходом, сделанным для любого конкретного потока (см. http://en.cppreference.com/w/cpp/io/c для сводки потоков C I/O)

В вашем случае stdout запускается неориентированным, и, выполняя первый printf, вы устанавливаете его узким. После того, как он узкий, он застрял, и wprintf не удается (проверьте его код возврата!). Единственный способ изменить поток C - это freopen он, который не работает с stdout. Вот почему 3 и 4 не печатались.

Различия между 1 и 3 заключаются в том, что 1 является узкой выходной функцией, которая использует спецификатор узких строк% s: он считывает байты из массива char и отправляет байты в поток байтов. 3 - широкая функция вывода с узким указателем преобразования строк% s: сначала он считывает байты из массива char и mbtowc их в wchar_t s, а затем отправляет wchar_t в широкий поток, который затем wctomb их в байты или многобайтовые последовательности, которые затем вставляются в стандартную версию с помощью write

Наконец, если widestr находится в utf16, вы должны использовать Windows, и все ставки отключены; поддержка ASCII на этой платформе очень мало. Вы можете также использовать WinAPI и использовать его с помощью стандартного С++ 11 для некоторых вещей в Unicode и даже делать этот вывод C с волшебными словами _setmode(_fileno(stdout), _O_U16TEXT);, которые обсуждались достаточно часто)

Ответ 3

Ответы на вопросы 1 и 2 находятся в документации. Любой хороший набор документации будет делать. Говорят, cppreference очень хорошо.

Как и в случае с 3, в стандарте языка не указывается какая-либо конкретная кодировка для строк или какой-либо конкретный размер wchar_t. Вам нужно проконсультироваться с документацией для вашей реализации, а не для собственно языка (хотя писать код, зависящий от реализации, редко бывает целесообразным).