|
|||||||
Как в Qt работать с русскими именами файлов и директорий под Windows
Время создания: 23.09.2018 16:29
Автор: xintrea
Текстовые метки: qt, qt5, кодировка, локаль, имя, файл, директория, открыть, читать, писать, windows
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Локализация
Запись: xintrea/mytetra_syncro/master/base/1537709346z7j60byo93/text.html на raw.github.com
|
|||||||
|
|||||||
Всемирно известная компания Microsoft отличается большой оригинальностью в области стандартов. Иначе как объяснить появление операционной системы, в которой кодировка имен файлов и кодировка консоли не совпадают? В русской редакции Windows есть следующая застарелая проблема: в качестве кодировки локали, а, следовательно, и кодировки имен файлов и директорий, принята кодировка CP 1251. Однако кодировкой консоли является CP 866. Как с этим жить в Qt? Проблема в том, что в Qt5 не предусмотрено разделение на кодировку локали и кодировку консоли. Если в русскоязычной Windows выполнить следующий код: QTextCodec::setCodecForLocale(QTextCodec::codecForName("CP 866")); то после его выполнения вывод в консоль русских строк будет работать правильно. То есть, вывод через qDebug(), qInfo(), qWarning(), qCritical(), qFatal() начнет работать сразу, без всяких ухищрений: qDebug() << "Привет, мир!"; А вывод через cout нужно оформлять немного сложнее, учитывая что кодировка исходников UTF-8: cout << QString::fromUtf8("Привет, ").toLocal8Bit().data(); QString world="мир!"; cout << world.toLocal8Bit().data(); В любом случае, в консоли станут появляться правильные русские символы. Однако, если установлена кодировка CP 866, все действия с файлами и директориями, в именах которых присутствуют русские символы, не будут работать в таких классах как QFile и QDir. Да и низкоуровневые функции fopen() / fclose() тоже не смогут работать с русскоязычными именами файлов. Если же установить в программе кодек на кодировку 1251: QTextCodec::setCodecForLocale(QTextCodec::codecForName("CP 1251")); то сразу заработают файловые операции через QFile и QDir над файлами и директориями, в именах которых присутствуют национальные русскоязычные символы. Функции fopen() / fclose() тоже заработают, если имена файлов указывать через QString- методы .toLocal8Bit().data(). Проблема в том, что кодек локали в Qt всего один. И он глобальный, и устанавливается вызовом QTextCodec::setCodecForLocale(имяКодека). В таких условиях невозможно одновременно работать с разными кодировками для локали и консоли. Что же делать? Вариантов немного. Вариант 1. Забить на кодировку консоли. Выставить кодировку локали как CP 1251 и спокойно работать. Да, в консоль будут сыпаться кракозябры, но хотя бы весь функционал программы по работе с файлами будет нормально работать. Вариант 2. Выставить кодировку локали как CP 1251. А чтобы сообщения сыпались в правильной кодировке, можно переопределить хендлер консольных сообщений. В нем можно перед печатью в консоль установить кодировку локали в CP 866, а при выходе вернуть в CP1251. Код будет примерно такой: // Установка хендлера void setDebugMessageHandler() { qDebug() << "Debug message before set message handler"; qDebug() << "Set alternative handler myMessageOutput() for debug message"; #if QT_VERSION < 0x050000 qInstallMsgHandler(myMessageOutput); #else qInstallMessageHandler(myMessageOutput); #endif qDebug() << "Debug message after set message handler"; } // Обработчик (хендлер) вызовов qDebug() и прочих видов сообщений // Внутри этого обработчика нельзя использовать вызовы qDebug(), т. к. получится рекурсия #if QT_VERSION < 0x050000 void myMessageOutput(QtMsgType type, const char *msg) #else void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msgText) #endif { #if QT_VERSION >= 0x050000 Q_UNUSED(context); #endif #if QT_VERSION < 0x050000 QString msgText( QString::fromUtf8(msg) ); #endif switch (type) { case QtDebugMsg: smartPrintDebugMessage("[DBG] "+msgText+"\n"); break; case QtWarningMsg: smartPrintDebugMessage("[WRN] "+msgText+"\n"); break; case QtCriticalMsg: smartPrintDebugMessage("[CRERR] "+msgText+"\n"); break; case QtFatalMsg: smartPrintDebugMessage("[FTERR] "+msgText+"\n"); abort(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) case QtInfoMsg: smartPrintDebugMessage("[INF] "+msgText+"\n"); break; #endif } } void smartPrintDebugMessage(QString msg) { // Установка кодека для нормальной работы консоли QTextCodec::setCodecForLocale(QTextCodec::codecForName("CP 866")); QTime currTime = QTime::currentTime(); QString timeText=currTime.toString("hh:mm:ss"); msg=timeText+" "+msg; cout << msg.toLocal8Bit().data();
// Установка кодека для нормальной работы локали QTextCodec::setCodecForLocale(QTextCodec::codecForName("CP 1251")); } В начале программы вызывается функция setDebugMessageHandler() , после чего дальнейшая работа идет стандартными средствами. Файлы с русскими именами будут открываться, а в консоль будут сыпаться сообщения с правильными русскими символами. Для операционных систем Linux уже давно стандартом является кодировка UTF-8, и такой свистопляски не требуется. А для создания кроссплатформенного решения, нужно условной компиляцией устанавливать кодек "UTF-8" и все. |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|