Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.138.199.50] |
|
Сообщ.
#1
,
|
|
|
Создаю консольные прикладные программы в Linux и в Windows (32bit, 64bit).
Основная рабочая среда - Qt и Linux (32bit, 64bit). Соответственно кириллица набирается и выводится в UTF-8. Проблема состоит в том, что часто созданные программы приходится использовать и в Windows (32bit, 64bit). Устав пересохранять код программ то в UTF-8, то в CP1251, решил сделать универсальный код с использованием модуля iconv.h Проблема решилась но только для Windows 32bit. Для Windows 64bit к моему большому удивлению нет готового дистрибутива Qt со всеми необходимыми программами - надо чуть ли не руками все до копейки собирать. Моей квалификации здесь не хватает, а 64bit программы нужны, т.к. могут использовать в работе RAM и 8Гб, и 16Гб, и 32Гб (комбинаторные задачи). Тактику выбрал такую: отлаживаю код в Windows Qt 32bit и потом уже отлаженный код компилирую и собираю в tdm64-gcc-10.3.0.exe Команда для сборки в консоли такая: c:\TDM-GCC-64\bin\x86_64-w64-mingw32-gcc.exe -m64 -std=c11 -O3 c:\Proj\Task\main.c Проблема состоит в том, что 64bit компилятор TDM-GCC-64 не понимает, что такое модуль iconv.h, несмотря на присутствие библиотеки libiconv-2.dll Найденных в сети советов немного и они не работают. Может кто сталкивался и знает решение проблемы? P.S. В Linux нет никаких проблем и с командной строкой. Все отлично компилируется командой clang -m64 -std=c11 -O3 /home/user/Proj/Task/main.c |
Сообщ.
#2
,
|
|
|
Цитата mkudritsky @ Не проще ль в консоли поставить 65001 кодовую страницу? Работать будет только с 10-ки, правда. И консольного шрифта с полной поддержкой юникода, насколько мне известно, не существует, но для подмножества символов из общего набора в UTF-8 и ANSI-1251 это не проблема. Устав пересохранять код программ то в UTF-8, то в CP1251, решил сделать универсальный код с использованием модуля iconv.h Добавлено std::cout << u8"Это строка в UTF8"; D:\Work\DelMe>chcp 65001 Active code page: 65001 D:\Work\DelMe>a Это строка в UTF8 D:\Work\DelMe> |
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Не проще ль в консоли поставить 65001 кодовую страницу? Работать будет только с 10-ки, правда. Разве? Вроде ещё в WinXP можно было, нужно было только шрифт сменить с дефолтного на Lucida. |
Сообщ.
#4
,
|
|
|
Господа, спасибо за советы!
Надо бы разобраться в кодовой странице 65001 и как ее использовать в программе C/C++. Займусь этим! Но вопрос про терминал я задал просто для сокращения своего поста. Тут на самом деле в ряде консольных программ помимо терминала еще есть требование выводить результаты в текстовые файлы в кодировке только CP1251 и в Linux, и в Windows. И использование iconv.h мне показалось самым элегантным. А может помимо tdm64-gcc-10.3.0.exe есть еще какие-нибудь 64bit компиляторы C/C++ для Windows? Может в этих альтернативных компиляторах есть нормальный модуль iconv.h? Кстати, мне еще не понравилось, что tdm64 не знает макроса WINVER, а я его использую для выяснения среды исполнения программы (Linux или Windows) и приходится использовать: -DWINVER P.S. На всякий случай приведу код, который использую для вывода в консоль кирилицы (для вывода в txt-файлы кириллицы в CP1251 позже приведу): #include <string.h> #ifndef WINVER #include <sys/sysinfo.h> #else #include <sysinfoapi.h> #endif #include <locale.h> #include <iconv.h> // Информация об основных параметрах ОС (главным образом - RAM) #ifndef WINVER struct sysinfo osInfo; char cSl = '/'; #else MEMORYSTATUSEX osInfo; char cSl = '\\'; #endif //--------------------------------------------------------------------------- char *Utf8ToWin(const char *cIn) { // Преобразование строки cIn из кодировки utf-8 в кодировку cp1251 // При преобразовании длина строки cp1251 не больше исходной длины строки utf-8 // После использования выходной строки ее надо освободить функцией free() // Длина входной строки (без \0) size_t iIn = strlen(cIn); // Работа со строками ненулевой длины if (!iIn) return NULL; // Открытие преобразования iconv_t cd_code = iconv_open("cp1251", "utf-8"); // Длина выходной строки и статус преобразования size_t iOut = iIn, iConv; // Выходная строка, указатель на элементы скрок при преобразовании (входной и выходной) char *cOut = (char *)calloc(iOut + 1, sizeof(char)), *cIn1 = (char *)cIn, *cOut1 = cOut; // Заполнение выходной строки символами конца строки /0 memset(cOut, 0, iOut * sizeof(char)); // Преобразование строки из utf-8 в cp1251 iConv = iconv(cd_code, &cIn1, &iIn, &cOut1, &iOut); // Закрытие преобразования iconv_close(cd_code); if (iConv) { if (cOut) free(cOut); return NULL; // Если было хотя бы одно неудачное преобразование } // Вывод результата return cOut; } //--------------------------------------------------------------------------- // Вывод содержимого строки sOut на консоль вне зависимости от OS void PrintCons(const char* cIn) { char* cOut; if (cSl == '/') cOut = (char *)cIn; // Linux else cOut = Utf8ToWin(cIn); // Windows printf("%s", cOut); if (cOut && (cSl != '/')) free(cOut); } //--------------------------------------------------------------------------- // Вывод содержимого строки cIn в файл в кодировке cp1251 void PrintFile(FILE *fOut, const char* cIn) { char* cOut; cOut = Utf8ToWin(cIn); if (cOut) { fprintf(fOut, "%s", cOut); free(cOut); } } //--------------------------------------------------------------------------- |
Сообщ.
#5
,
|
|
|
Цитата korvin @ Шрифт не проблема, проблема – UTF-8 страница. Не уверен, но вроде бы даже 7-ка не поддерживала юникод в консоли. Разве? Вроде ещё в WinXP можно было, нужно было только шрифт сменить с дефолтного на Lucida. Добавлено Цитата mkudritsky @ Если тебе Qt, то вариантов в общем-то и нет. В целом же компиляторов вагон, та же Visual Studio без проблем компилит, причём не знаю как сейчас, но ещё недавно это был единственный компилятор, полностью поддерживающий C++20 на уровне языка и библиотеки.А может помимо tdm64-gcc-10.3.0.exe есть еще какие-нибудь 64bit компиляторы C/C++ для Windows? Что да iconv, то его базовый функционал покрывается стандартными средствами C++. Но нельзя отрицать, что пользоваться iconv несколько удобнее, и возможностей там поболе. + поддержка чистого C. Впрочем, для Qt по любому C++ потребуется. |
Сообщ.
#6
,
|
|
|
Юникод в консоли поддерживает уже XP. Но только UCS-2.
|
Сообщ.
#7
,
|
|
|
Еще немного поковырялся со всем этим хозяйством.
У меня сложилось мнение, что компилятор tdm64-gcc-10.3.0.exe является выжимкой от проекта: http://mingw-w64.org или сразу в загрузки https://sourceforge.net/projects/mingw-w64/...gw-w64-release/ Короче: снес я TDM64 и установил mingw-w64. (Кстати, они конфликтуют друг с другом и мне удалось поставить только по принципу "или или"). В mingw-w64 изначально модуль iconv есть. Правда, это не спасает ситуацию: c:\Temp>gcc.exe -D_USE_MATH_DEFINES -m64 -std=c11 -O3 -liconv main.c c:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw3 2/lib/../lib/libiconv.a(localcharset.o):localcharset.c:(.text+0x63): undefined reference to `__imp_GetACP' collect2.exe: error: ld returned 1 exit status Проблема давнишняя, как я понял из поиска в Интернете, и решения пока не имеет. Попутно замечу, что помимо нерабочего iconv тут еще и не все макросы работают. Например, для M_PI и M_PI_2 пришлось при компиляции использовать -D_USE_MATH_DEFINES Что крайне удивительно: в Qt используется тот же самый mingw и нет никаких проблем ни с iconv, ни с M_PI В общем, всем спасибо за советы. Но пока, видимо, придется компилировать по-старинке, меняя кодировку программ из utf-8 в cp1251 и обратно. Или использовать code page 65001. |
Сообщ.
#8
,
|
|
|
Цитата mkudritsky @ Проблема состоит в том, что 64bit компилятор TDM-GCC-64 не понимает, что такое модуль iconv.h, несмотря на присутствие библиотеки libiconv-2.dll Найденных в сети советов немного и они не работают. у тебя преобразователь для вывода в консоль не работает и всё ? Если так, можно попробовать сделать условную компиляцию и преобразовать строки по-разному для Виндус и Linux. У Виндуса есть функции: ::WideCharToMultiByte(...); ::MultiByteToWideChar(...); И ещё есть: ::OemToCharBuff(...); ::CharToOemBuff(...); Используя эти функции можно делать необходимые преобразования кодировок UTF-8, CP1251, OEM, UTF-16. --- Основная твоя система, как я понял, Linux. Пусть все строки будут в UTF-8. Т.е. для вывода в консоль Виндуса надо сделать дополнительный преобразователь UTF-8 -> OEM. И всё. |
Сообщ.
#9
,
|
|
|
Народ, добрый день!
Наконец-то я победил этот злосчастный модуль iconv Теперь можно писать универсальные программы в кодировке UTF8, а вывод (консоль, файл) осуществлять либо в CP1251 (Windows), либо в UTF8 (Linux). Управление при компиляции - макрос WINVER (ключ компилятора -DWINVER). Корень проблемы - ошибки в компиляторе mingw64, которые, увы, все еще не исправлены. Как надо правильно компилировать - подсмотрел в консоли сборки Qt (где все нормально собирается с модулем iconv). Собирать все можно и в Си, и в C++. Итак, вот мой пример программы "Hello world" где есть все нужные функции (даже лишние): // Компиляция: // x86_64-w64-mingw32-gcc.exe -m64 -std=c11 -O3 -DWINVER c:\Tmp\main.c // clang -m64 -std=c11 -O3 main.c // С модулем iconv // x86_64-w64-mingw32-gcc.exe -c -pipe -fno-keep-inline-dllexport -D_USE_MATH_DEFINES -m64 -std=c11 -O3 -Wall -Wextra -DUNICODE -DWINVER c:\Tmp\main.c // x86_64-w64-mingw32-gcc.exe -Wl,-s -Wl,-subsystem,console -mthreads main.o -liconv #include <stdio.h> #include <stdlib.h> #include <time.h> #include <locale.h> #include <math.h> #include <string.h> #include <iconv.h> #ifndef WINVER char cSl = '/'; #else char cSl = '\\'; #endif //--------------------------------------------------------------------------- int iRand() { // Аналог rand() для юродиевых версий "любимой" Windows, где RAND_MAX=32767 int iR = rand(); if ( RAND_MAX == 32767 ) { // 32767=2**15-1 iR <<= 15; // iR = iR*32768 (32768=2**15) int iL = rand(); iR |= iL; // iR = rand()*(2**15)+rand; } return iR; } //--------------------------------------------------------------------------- int iRandom(int iMin, int iMax) { // Возвращает случайное целое число между iMin и iMax. Границы могут генерироваться if (iMin < iMax) return iMin + iRand() % (iMax - iMin + 1); else if ( iMin == iMax ) return iMin; else return iMin - 1; // Если границы заданы неверно, то возвращается такое число } //--------------------------------------------------------------------------- double dbRandom(double dbMin, double dbMax) { // Возвращает случайное вещественное число между dbMin и dbMax. Границы могут генерироваться if (dbMin < dbMax) { if (RAND_MAX == 32767) return dbMin + ((dbMax - dbMin) * (double)iRand()) / 1073741823.0; // = double(2**30-1) else return dbMin + ((dbMax - dbMin) * (double)iRand()) / (double)RAND_MAX; } else if (dbMin == dbMax) return dbMin; else return dbMin - 1.0; } //--------------------------------------------------------------------------- char *Utf8ToWin(const char *cIn) { // Преобразование строки cIn из кодировки utf-8 в кодировку cp1251 // При преобразовании длина строки cp1251 не больше исходной длины строки utf-8 // После использования выходной строки ее надо освободить функцией free() // Длина входной строки (без \0) size_t iIn = strlen(cIn); // Работа со строками ненулевой длины if (!iIn) return NULL; // Открытие преобразования iconv_t cd_code = iconv_open("cp1251", "utf-8"); // Длина выходной строки и статус преобразования size_t iOut = iIn, iConv; // Выходная строка, указатель на элементы скрок при преобразовании (входной и выходной) char *cOut = (char *)calloc(iOut + 1, sizeof(char)), *cIn1 = (char *)cIn, *cOut1 = cOut; // Заполнение выходной строки символами конца строки /0 memset(cOut, 0, iOut * sizeof(char)); // Преобразование строки из utf-8 в cp1251 iConv = iconv(cd_code, &cIn1, &iIn, &cOut1, &iOut); // Закрытие преобразования iconv_close(cd_code); if (iConv) { if (cOut) free(cOut); return NULL; // Если было хотя бы одно неудачное преобразование } // Вывод результата return cOut; } //--------------------------------------------------------------------------- // Вывод содержимого строки sOut на консоль вне зависимости от OS void PrintCons(const char* cIn) { char* cOut; if (cSl == '/') cOut = (char *)cIn; // Linux else cOut = Utf8ToWin(cIn); // Windows printf("%s", cOut); if (cOut && (cSl != '/')) free(cOut); } //--------------------------------------------------------------------------- // Вывод содержимого строки cIn в файл в кодировке cp1251 void PrintFile(FILE *fOut, const char* cIn) { char* cOut; cOut = Utf8ToWin(cIn); if (cOut) { fprintf(fOut, "%s", cOut); free(cOut); } } //--------------------------------------------------------------------------- int main(int argc, char *argv[]) { // Файл для вывода сообщения в CP1251 FILE *Fdat; // Переключение локали для вывода кирилицы в терминальном окне setlocale(LC_CTYPE, ""); // Открытие файла или создание нового файла if ((Fdat = fopen("test.txt", "wt")) == NULL) { PrintCons("Файл не удается открыть\n"); return -1; } // Начало работы программы. Вывод начального сообщения PrintCons("Приветствие! Запись сообщения в файл test.txt\n"); // Запись сообщения в файл в кодировке CP1251 PrintFile(Fdat, "Приветствие! Запись сообщения в файл test.txt\n"); // Закрытие файла fclose(Fdat); // Окончание работы программы PrintCons("Программа завершена!\n"); return 0; } Увы, в Windows компиляция получилась "двухэтажной". Одной строкой exe-шник мне собрать не удалось. Ну а в Linux все гораздо проще - все собирается без танцев с бубном либо в gcc, либо в clang одной строкой. P.S. Я не шибко искушен в C/C++ и посему почти уверен - из строк компиляции можно выкинуть половину ключей, а то и объединить две строки в одну. Может даже получится убрать статическую библиотеку и заменить ее на динамическую. Также просьба посмотреть на функции Utf8ToWin, PrintCons, PrintFile - может грубые ошибки есть? |
Сообщ.
#10
,
|
|
|
Нашел ошибку в приведенных мной примерах функций Utf8ToWin, PrintCons и PrintFile.
Просто мое (полу)хобби - решение комбинаторных задач типа Задачи Коммивояжера. А ряд этих задач влегкую "съедает" и 8Гб, и 16Гб, и 64Гб, и 128Гб... оперативной памяти. То есть для вывода сообщений мне в программах попросту не хватило свободной RAM! Итак, скорректированный код: // clang -m32 -O3 -lm main.c // Компиляция: x86_64-w64-mingw32-gcc.exe -m32 -std=c11 -O3 c:\Temp\main.c // Автономным компилятором в две строки: // gcc.exe -c -pipe -fno-keep-inline-dllexport -m32 -std=c11 -O3 -Wall -Wextra -DUNICODE -DWINVER -D_USE_MATH_DEFINES c:\Temp\main.c // gcc.exe -Wl,-s -Wl,-subsystem,console -mthreads main.o -liconv //--------------------------------------------------------------------------- char *Utf8ToWin(const char *cIn) { // Преобразование строки cIn из кодировки utf-8 в кодировку cp1251 // При преобразовании длина строки cp1251 не больше исходной длины строки utf-8 // После использования выходной строки ее надо освободить функцией free() // Длина входной строки (без \0) size_t iIn = strlen(cIn); // Работа со строками ненулевой длины if (!iIn) return NULL; // Открытие преобразования iconv_t cd_code = iconv_open("cp1251", "utf-8"); // Длина выходной строки и статус преобразования size_t iOut = iIn, iConv; // Выходная строка, указатель на элементы скрок при преобразовании (входной и выходной) char* cOut = NULL; cOut = (char *)calloc(iOut + 1, sizeof(char)); if (!cOut) { return NULL; } char *cIn1 = (char *)cIn, *cOut1 = cOut; // Заполнение выходной строки символами конца строки /0 memset(cOut, 0, iOut * sizeof(char)); // Преобразование строки из utf-8 в cp1251 iConv = iconv(cd_code, &cIn1, &iIn, &cOut1, &iOut); // Закрытие преобразования iconv_close(cd_code); if (iConv) { if (cOut) free(cOut); return NULL; // Если было хотя бы одно неудачное преобразование } // Вывод результата return cOut; } //--------------------------------------------------------------------------- // Вывод содержимого строки sOut на консоль вне зависимости от OS void PrintCons(const char* cIn) { char* cOut = NULL; if (cSl == '/') cOut = (char *)cIn; // Linux else cOut = Utf8ToWin(cIn); // Windows if (cOut) { printf("%s", cOut); if (cSl != '/') free(cOut); } else { printf("PrintCons: error convert string!\n"); } return; } //--------------------------------------------------------------------------- // Вывод содержимого строки cIn в файл в кодировке cp1251 void PrintFile(FILE *fOut, const char* cIn) { char* cOut = NULL; cOut = Utf8ToWin(cIn); if (cOut) { fprintf(fOut, "%s", cOut); free(cOut); } return; } //--------------------------------------------------------------------------- |
Сообщ.
#11
,
|
|
|
memset() после calloc() не нужен.
|