Как я печатаю строку, до которой __ расширяется ФАЙЛ __ правильно?

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

#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. Чем кодирование используется для строки, данной __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__ );

Это повреждается в случае, если текущий исходный файл имеет имя, которое содержит символы, которые не могут быть представлены текущей кодовой страницей.

10
задан Frerich Raabe 20 July 2010 в 14:39
поделиться

5 ответов

Можно использовать оператор вставки токенов, например, так:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define WFILE WIDEN(__FILE__)

int main() {
    wprintf("%s\n", WFILE);
    return 0;
}
11
ответ дан 4 December 2019 в 01:55
поделиться

__FILE__ всегда будет расширяться до литерала символьной строки, поэтому по сути он будет совместим с char const*. Это означает, что у реализации компилятора нет другого выбора, кроме как использовать raw байтовое представление имени исходного файла, как оно представляется во время компиляции.

Является ли это чем-то разумным в текущей локали или нет, не имеет значения, вы можете иметь имя исходного файла, содержащее практически мусор, до тех пор, пока ваша система времени выполнения и компилятор принимают его как допустимое имя файла.

Если у вас, как у пользователя, другая локаль с другой кодировкой, чем используется в вашей файловой системе, вы увидите много ???? и т.п..

Но если обе ваши локали согласны с кодировкой, то обычного printf будет достаточно, и ваш терминал (или то, что вы используете для просмотра вывода) сможет правильно вывести символы.

Короче говоря, это будет работать, только если ваша система согласована с кодировкой. В противном случае вам не повезло, поскольку угадать кодировку - довольно сложная задача.

1
ответ дан 4 December 2019 в 01:55
поделиться

Что касается кодировки, я собираюсь предположить, что это то, что используется файловой системой, возможно, Unicode.

Что касается работы с этим, как насчет того, чтобы изменить код, например:

#define TRACE_BEACON activeLogFile()->write( FixThisString(__FILE__ )); 

std::string FixThisString(wchar_t* bad_string) { .....}

(Реализация FixThisString оставлена ​​в качестве упражнения для ученика.)

-1
ответ дан 4 December 2019 в 01:55
поделиться

Лучшее решение - использовать имена исходных файлов в переносимом наборе символов имени файла [A-Za-z0-9 ._-] . Поскольку Windows не поддерживает UTF-8, нет возможности для представления произвольных символов, отличных от ASCII, в обычных строках без зависимости от настроенного локального языка.

gcc, вероятно, это не волнует; он обрабатывает все имена файлов как 8-битные строки, и поэтому, если имя файла доступно для gcc, его имя будет представимым. (Я знаю, что cygwin по умолчанию предоставляет среду UTF-8, а современный * nix обычно будет UTF-8.) Для MSVC вы можете использовать препроцессор для добавления L к расширению ] __ FILE __ и используйте % ls для его форматирования.

-1
ответ дан 4 December 2019 в 01:55
поделиться

В MSVC можно включить Unicode и получить строки в кодировке UTF-16. Это где-то в свойствах проекта. Кроме того, вы должны просто использовать wcout / cout, а не printf / wprintf. Windows нуждался в Unicode до того, как появился Unicode, поэтому у них была настраиваемая кодировка многобайтовых символов, которая используется по умолчанию. Однако Windows поддерживает UTF16 - например, C #.

#include <iostream>

int main() {
    std::wcout << __WFILE__;
}
-1
ответ дан 4 December 2019 в 01:55
поделиться