You do not need to set the locale or set any special modes in the file if you just want to use fprintf. You just need to use UTF-8 encoded strings.
#include <cstdio> #include <codecvt> int main() { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> convert; std::string utf8_string = convert.to_bytes(L" 日本国"); if(FILE *f = fopen("tmp","w")) fprintf(f,"%s\n",utf8_string.c_str()); }
Save the program as UTF-8 with a signature or UTF-16 (i.e. do not use UTF-8 without a signature, otherwise VS will not output the correct string literal). A file written by the program will contain a version of this line of UTF-8. Or you can do:
int main() { if(FILE *f = fopen("tmp","w")) fprintf(f,"%s\n"," 日本国"); }
In this case, you should save the file as UTF-8 without a signature, because you want the compiler to think that the source encoding is the same as the executing encoding ... This is a bit of a hack that depends on the compiler, IMO, behavior violation.
You can basically do the same with any other API for writing narrow characters to a file, but note that none of these methods work for writing UTF-8 to the Windows console. Since the C runtime and / or console is a bit broken, you can write UTF-8 directly to the console by running SetConsoleOutputCP (65001) and then using one of the puts
functions.
If you want to use wide characters instead of narrow characters, then locale-based methods and file descriptor setting modes can come into play.
#include <cstdio> #include <fcntl.h> #include <io.h> int main() { if(FILE *f = fopen("tmp","w")) { _setmode(_fileno(f), _O_U8TEXT); fwprintf(f,L"%s\n",L" 日本国"); } }
#include <fstream> #include <codecvt> int main() { if(auto f = std::wofstream("tmp")) { f.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>)); // assumes wchar_t is UTF-16 f << L" 日本国\n"; } }