代码之家  ›  专栏  ›  技术社区  ›  Frerich Raabe

如何打印扩展到正确位置的字符串?

  •  10
  • Frerich Raabe  · 技术社区  · 16 年前

    考虑这个程序:

    #include <stdio.h>
    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. 对由给定的字符串使用什么编码 一个文件 ?看起来,至少在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__ );
    

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

    5 回复  |  直到 14 年前
        1
  •  11
  •   Hans Passant    14 年前

    使用可以使用标记粘贴运算符,如下所示:

    #define WIDEN2(x) L ## x
    #define WIDEN(x) WIDEN2(x)
    #define WFILE WIDEN(__FILE__)
    
    int main() {
        wprintf("%s\n", WFILE);
        return 0;
    }
    
        2
  •  1
  •   Jens Gustedt    16 年前

    __FILE__ 将一直扩展到 窄字符串文字量 因此,在本质上,它将与 char const* . 这意味着编译器实现除了使用 未经加工的 源文件名在编译时的字节表示形式。

    不管这在当前区域设置中是否合理,只要运行时系统和编译器接受它作为有效的文件名,就可以有一个基本上包含垃圾的源文件名。

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

    但是 如果 两个地区都同意编码,一个普通的 printf 应该足够了,并且您的终端(或者您用来查看输出的任何东西)应该能够正确地打印字符。

    因此,简短的答案是,只有当您的系统采用一致的W.R.T编码时,它才能工作。否则你就走运了,因为猜测编码是一项相当困难的任务。

        3
  •  -1
  •   James Curran    16 年前

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

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

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

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

        4
  •  -1
  •   R.. GitHub STOP HELPING ICE    16 年前

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

    gcc可能不关心;它将所有文件名视为8bit字符串,因此如果gcc可以访问该文件名,它的名称将是可表示的。(我知道cygwin默认提供了一个utf-8环境,而modern*nix通常是utf-8。)对于msvc,您可以使用预处理器进行预处理。 L 扩大 __FILE__ 使用 %ls 格式化它。

        5
  •  -1
  •   Puppy    16 年前

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

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