如何正确打印__FILE__扩展的字符串?

考虑这个程序:

#include  int main() { printf("%s\n", __FILE__); return 0; } 

根据文件的名称,此程序可以工作 – 或不工作。 我面临的问题是我想以编码安全的方式打印当前文件的名称。 但是,如果文件具有无法在当前代码页中表示的有趣字符,则编译器会发出警告(这是正确的):

 ?????????.c(3) : warning C4566: character represented by universal-character-name '\u043F' cannot be represented in the current code page (1252) 

我该如何解决这个问题? 我想将__FILE__给出的字符串存储在例如UTF-16中,这样我就可以在运行时在任何其他系统上正确打印它(通过将存储的UTF-16表示转换为运行时系统使用的任何表示)。 为此,我需要知道:

  1. __FILE__给出的字符串使用了什么编码? 看来,至少在Windows上,使用了当前的系统代码页(在我的例子中是Windows-1252) – 但这只是猜测。 这是真的?
  2. 如何在构建时将源代码中的字符串的UTF-8(或UTF-16)表示存储起来?

我的真实用例:我有一个跟踪当前程序执行的宏,将当前源代码/行号信息写入文件。 它看起来像这样:

 struct LogFile { // Write message to file. The file should contain the UTF-8 encoded data! void writeMessage( const std::string &msg ); }; // Global function which returns a pointer to the 'active' log file. LogFile *activeLogFile(); #define TRACE_BEACON activeLogFile()->write( __FILE__ ); 

如果当前源文件的名称包含当前代码页无法表示的字符,则会中断此操作。

使用可以使用令牌粘贴操作符,如下所示:

 #define WIDEN2(x) L ## x #define WIDEN(x) WIDEN2(x) #define WFILE WIDEN(__FILE__) int main() { wprintf("%s\n", WFILE); return 0; } 

__FILE__将始终扩展为字符串文字 ,因此本质上它将与char const*兼容。 这意味着编译器实现除了使用源文件名的原始字节表示之外没有太多其他选择,因为它在编译时呈现。

无论这在当前语言环境中是否合理,都无关紧要,只要您的运行时系统和编译器将其作为有效文件名接受,您就可以拥有基本上包含垃圾的源文件名。

如果您作为用户使用不同的语言环境,其编码与文件系统中使用的编码不同,您会看到很多???? 或者一样的。

但是如果你的语言环境都同意编码,那么普通的printf应该足够了,你的终端(或者你用来查看输出的任何东西)应该能够正确地打印字符。

所以简短的回答是,它只有在您的系统与编码一致的情况下才有效。 否则你运气不好,因为猜测编码是一项相当困难的任务。

至于编码,我猜它是文件系统使用的,可能是Unicode。

至于处理它,如何改变你的代码就像:

 #define TRACE_BEACON activeLogFile()->write( FixThisString(__FILE__ )); std::string FixThisString(wchar_t* bad_string) { .....} 

(FixThisString的实现留给学生练习。)

最佳解决方案是在可移植文件名字符集[A-Za-z0-9._-]使用源文件名。 由于Windows不支持UTF-8,因此无法在普通字符串中表示任意非ASCII字符,而不依赖于您配置的本地语言。

gcc可能并不关心; 它将所有文件名视为8位字符串,因此如果gcc可以访问文件名,则其名称将是可表示的。 (我知道cygwin默认提供UTF-8环境,而现代* nix通常是UTF-8。)对于MSVC,您可能可以使用预处理器将L__FILE__扩展中并使用%ls来格式化它。

在MSVC中,您可以打开Unicode并获取UTF-16编码的字符串。 它位于某个项目属性中。 另外,你应该只使用wcout / cout而不是printf / wprintf。 在Unicode存在之前,Windows需要Unicode,因此它们具有自定义多字节字符编码,这是默认设置。 但是,Windows确实支持UTF16-例如,C#。

 #include  int main() { std::wcout << __WFILE__; }