C源文件中的可移植文字字符串

Portable literal strings in C source files

好的,我有这个:

1
2
3
4
5
6
7
8
9
10
11
AllocConsole();
SetConsoleOutputCP(CP_UTF8);
HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(consoleHandle,"aΕλληνικ?
"
, 10, NULL, NULL);
WriteConsoleW(consoleHandle, L"wΕλληνικ?
"
, 10, NULL, NULL);
printf("aΕλληνικ?
"
);
wprintf(L"wΕλληνικ?
"
);

现在的问题是,根据编码文件的不同,只保存了一些这样的文件。wprintf不起作用,但我已经知道为什么(破坏了Microsoft stdout实现,它只接受窄字符)。然而,我和另外三个有问题。如果我保存文件UTF-8没有签名(BOM)和使用MS VisualC++编译器,只有最后一个Prtuf工作。如果我想要使用ANSI版本,我需要增加字符(?)数到18:

1
2
WriteConsoleA(consoleHandle,"aΕλληνικ?
"
, 18, NULL, NULL);

我认为,writeconsolew不起作用,因为该字符串保存为utf-8字节序列,即使我明确要求将其存储为带L前缀的宽字符(utf-16),实现也很可能需要utf-16编码的字符串而不是utf-8。

如果我用UTF-8和BOM(应该是这样)保存它,那么writeconsolew就开始以某种方式工作了(???)其他一切都停止了(我明白了吗?而不是角色)。我需要将writeconsolea中的字符数减少到10,以保持相同的格式(否则我会得到8个额外的矩形)。基本上,WTF?

现在,让我们转到UTF-16(Unicode代码页1200)。仅适用于writeconsolew。writeconsolea中的字符数应为10,以保持格式精确。

以UTF-16大端模式(Unicode-代码页1201)保存不会更改任何内容。再说一遍,跆拳道?存储到文件时,字符串中的字节顺序不应该颠倒吗?

结论是字符串编译成二进制形式的方式取决于所使用的编码。因此,存储字符串的可移植和编译器独立的方法是什么?是否有一个预处理器可以在编译之前将一个字符串表示形式转换为另一个字符串表示形式,这样我就可以将文件存储为UTF-8格式,并且只需要将一些宏包装在UTF-16格式中所需的预处理字符串中。


答案就在这里。

引用:

It is impossible for the compiler to intermix UTF-8 and UTF-16
strings into the compiled output! So you have to decide for one source
code file:

  • either use UTF-8 with BOM and generate UTF-16 strings only (i.e.always use L prefix),
  • or UTF-8 without BOM and generate UTF-8 strings only (i.e. never use L prefix),
  • 7-bit ASCII characters are not involved and can be used with or without L prefix

唯一可移植且与编译器无关的方法是使用ASCII字符集和转义序列,因为无法保证任何编译器都会接受UTF-8编码的文件,并且对这些多字节序列的编译器处理可能会有所不同。


据我所知,我认为你至少有一些假设是错误的或不是100%正确的:

Now, the issue is that depending on the encoding file was saved as only some these works.

当然,因为编码决定了如何解释字符串文本。

wprintf never works, but I already know why (broken Microsoft stdout implementation, which only accepts narrow characters).

我从来没有听说过这个,但我很确定这取决于为您的程序设置的语言环境。我有几个工作项目,其中设置了一个区域设置,并且使用德语umlauts等可以很好地输出。

If I save file as UTF-8 without signature (BOM) and use MS Visual C++ compiler, only last printf works. If I want ANSI version working I need to increase character(?) count to 18:

这是因为ansi版本需要一个ansi字符串,而您传递的是一个utf-8编码的字符串(基于文件的编码)。输出仍然可以工作,因为控制台为您处理UTF-8转换——您实际上是在这里打印原始的UTF-8。

WriteConsoleW does not work, I assume, because the string is saved as UTF-8 byte sequence even I explicitly request it to be stored as wide-char (UTF-16) with L prefix and implementation most probably expects UTF-16 encoded string not UTF-8.

我不这么认为(尽管我也不确定为什么它也不起作用)。您是否尝试设置一些易于查找的字符串并在生成的二进制文件中查找它?我相当肯定它确实是用UTF-16编码的。我假设由于缺少bom,编译器可能会将整个内容解释为一个窄字符串,因此将utf-8转换错误。

If I save it in UTF-8 with BOM (as it should be), then WriteConsoleW starts to work somehow (???) and everything else stops (I get ? instead of a character). I need to decrease character count in WriteConsoleA back to 10 to keep formatting the same (otherwise i get 8 additional rectangles). Basically, WTF?

这正是我上面描述的。现在,宽字符串被正确编码,因为编译器现在知道文件是UTF-8格式的,而不是ANSI格式的(或者一些代码页)。窄字符串也被正确地转换为正在使用的区域设置。

总的来说,没有独立的编码方式可以做到这一点,除非您提前使用正确的代码页和/或UTF代码来逃避所有的事情。我会坚持使用带BOM的UTF-8,因为我认为所有当前的编译器都能够正确地读取和解释该文件(除了微软的资源编译器;尽管我还没有尝试使用UTF-8提供2012版本)。

编辑:

使用类比:

实际上,您将原始图像保存到一个文件中,并且希望它能够正常工作,无论其他程序是否尝试将其读取为灰度、调色板或全色图像。这不起作用(尽管差异较小)。