На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> TDM-GCC-64 и iconv в Windows , Универсальный код в Debian и в Windows
    Создаю консольные прикладные программы в 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
    Команда для сборки в консоли такая:
    ExpandedWrap disabled
      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 нет никаких проблем и с командной строкой. Все отлично компилируется командой
    ExpandedWrap disabled
      clang -m64 -std=c11 -O3 /home/user/Proj/Task/main.c
      Цитата mkudritsky @
      Устав пересохранять код программ то в UTF-8, то в CP1251, решил сделать универсальный код с использованием модуля iconv.h
      Не проще ль в консоли поставить 65001 кодовую страницу? Работать будет только с 10-ки, правда. И консольного шрифта с полной поддержкой юникода, насколько мне известно, не существует, но для подмножества символов из общего набора в UTF-8 и ANSI-1251 это не проблема.

      Добавлено
      ExpandedWrap disabled
        std::cout << u8"Это строка в UTF8";
      ExpandedWrap disabled
        D:\Work\DelMe>chcp 65001
        Active code page: 65001
         
        D:\Work\DelMe>a
        Это строка в UTF8
        D:\Work\DelMe>
        Цитата Qraizer @
        Не проще ль в консоли поставить 65001 кодовую страницу? Работать будет только с 10-ки, правда.

        Разве? Вроде ещё в WinXP можно было, нужно было только шрифт сменить с дефолтного на Lucida.
          Господа, спасибо за советы!
          Надо бы разобраться в кодовой странице 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 позже приведу):
          ExpandedWrap disabled
            #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);
                }
            }
            //---------------------------------------------------------------------------
          Сообщение отредактировано: mkudritsky -
            Цитата korvin @
            Разве? Вроде ещё в WinXP можно было, нужно было только шрифт сменить с дефолтного на Lucida.
            Шрифт не проблема, проблема – UTF-8 страница. Не уверен, но вроде бы даже 7-ка не поддерживала юникод в консоли.

            Добавлено
            Цитата mkudritsky @
            А может помимо
            tdm64-gcc-10.3.0.exe
            есть еще какие-нибудь 64bit компиляторы C/C++ для Windows?
            Если тебе Qt, то вариантов в общем-то и нет. В целом же компиляторов вагон, та же Visual Studio без проблем компилит, причём не знаю как сейчас, но ещё недавно это был единственный компилятор, полностью поддерживающий C++20 на уровне языка и библиотеки.
            Что да iconv, то его базовый функционал покрывается стандартными средствами C++. Но нельзя отрицать, что пользоваться iconv несколько удобнее, и возможностей там поболе. + поддержка чистого C. Впрочем, для Qt по любому C++ потребуется.
              Юникод в консоли поддерживает уже XP. Но только UCS-2.
                Еще немного поковырялся со всем этим хозяйством.
                У меня сложилось мнение, что компилятор 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 есть.
                Правда, это не спасает ситуацию:
                ExpandedWrap disabled
                  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.
                Сообщение отредактировано: mkudritsky -
                  Цитата mkudritsky @
                  Проблема состоит в том, что 64bit компилятор TDM-GCC-64 не понимает, что такое модуль iconv.h, несмотря на присутствие библиотеки libiconv-2.dll
                  Найденных в сети советов немного и они не работают.

                  у тебя преобразователь для вывода в консоль не работает и всё ?
                  Если так, можно попробовать сделать условную компиляцию
                  и преобразовать строки по-разному для Виндус и Linux.
                  У Виндуса есть функции:
                  ExpandedWrap disabled
                     ::WideCharToMultiByte(...);
                     ::MultiByteToWideChar(...);

                  И ещё есть:
                  ExpandedWrap disabled
                    ::OemToCharBuff(...);
                    ::CharToOemBuff(...);


                  Используя эти функции можно делать необходимые преобразования
                  кодировок UTF-8, CP1251, OEM, UTF-16.
                  ---
                  Основная твоя система, как я понял, Linux.
                  Пусть все строки будут в UTF-8.
                  Т.е. для вывода в консоль Виндуса надо сделать дополнительный
                  преобразователь UTF-8 -> OEM.
                  И всё.
                  Сообщение отредактировано: ЫукпШ -
                    Народ, добрый день!
                    Наконец-то я победил этот злосчастный модуль iconv
                    Теперь можно писать универсальные программы в кодировке UTF8, а вывод (консоль, файл) осуществлять либо в CP1251 (Windows), либо в UTF8 (Linux).
                    Управление при компиляции - макрос WINVER (ключ компилятора -DWINVER).

                    Корень проблемы - ошибки в компиляторе mingw64, которые, увы, все еще не исправлены.
                    Как надо правильно компилировать - подсмотрел в консоли сборки Qt (где все нормально собирается с модулем iconv).
                    Собирать все можно и в Си, и в C++.

                    Итак, вот мой пример программы "Hello world" где есть все нужные функции (даже лишние):
                    ExpandedWrap disabled
                      // Компиляция:
                      // 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 - может грубые ошибки есть?
                      Нашел ошибку в приведенных мной примерах функций Utf8ToWin, PrintCons и PrintFile.
                      Просто мое (полу)хобби - решение комбинаторных задач типа Задачи Коммивояжера.
                      А ряд этих задач влегкую "съедает" и 8Гб, и 16Гб, и 64Гб, и 128Гб... оперативной памяти.

                      То есть для вывода сообщений мне в программах попросту не хватило свободной RAM! :D
                      Итак, скорректированный код:

                      ExpandedWrap disabled
                        // 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;
                        }
                        //---------------------------------------------------------------------------
                      Сообщение отредактировано: mkudritsky -
                        memset() после calloc() не нужен.
                        1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                        0 пользователей:


                        Рейтинг@Mail.ru
                        [ Script execution time: 0,0432 ]   [ 18 queries used ]   [ Generated: 6.12.22, 20:09 GMT ]