Рассмотрите эту программу:
#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 тому, что система во время выполнения использует). Для этого я должен знать:
__FILE__
? Кажется, что, по крайней мере, в Windows, кодовая страница существующей системы (в моем случае, Windows 1252) используется - но это просто предполагает. Действительно ли это верно?Мой реальный вариант использования: у Меня есть макрос, который прослеживает текущее выполнение программы, пишущий текущую информацию об исходном коде/номере строки в файл. Это похоже на это:
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*
. Это означает, что у реализации компилятора нет другого выбора, кроме как использовать raw байтовое представление имени исходного файла, как оно представляется во время компиляции.
Является ли это чем-то разумным в текущей локали или нет, не имеет значения, вы можете иметь имя исходного файла, содержащее практически мусор, до тех пор, пока ваша система времени выполнения и компилятор принимают его как допустимое имя файла.
Если у вас, как у пользователя, другая локаль с другой кодировкой, чем используется в вашей файловой системе, вы увидите много ???? и т.п..
Но если обе ваши локали согласны с кодировкой, то обычного 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. Windows нуждался в Unicode до того, как появился Unicode, поэтому у них была настраиваемая кодировка многобайтовых символов, которая используется по умолчанию. Однако Windows поддерживает UTF16 - например, C #.
#include <iostream>
int main() {
std::wcout << __WFILE__;
}