Процессор работает на основе сигналов, эти сигналы складываются в блоки, на основе этих блоков процессор интерпретирует некие операции над этими блоками сигналов. В таком ключе все хорошо работает пока не появляется человеческий фактор, а именно человеку нужно взаимодействовать с процессором. На заре компьютеров, Человеку достаточно было отображать сигнал в виде света, если есть сигнал, «лампочка» зажглась, если нет сигнала, «лампочка» не горит, но поскольку такое взаимодействие человека и компьютера, требовало специальных знаний обозначения сигнала (что значит «лампочка» в контексте программы) решено контекст делегировать на компьютер, а то есть на основе естественного языка передавать информацию человеку. С этого момента у программистов появилось новая «головная боль» проблема кодировок естественных языков, одна из первых и старых проблем программирования.

Немного теории


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

Современные программисты также как и учебники программирования, стали забывать что программист должен контролировать все устройства ввода вывода, а не операционная система. в связи с этим начинающие программисты не понимают почему программа которая написана корректно с точки зрения алгоритмов работает не правильно, то есть не отображает символы так как задумал программист.

Немного практики


В современном мире, почти все кодировки и отображение контролирует операционная система, чаще всего в Windows отображаются кракозябры, а в Linux все хорошо, и новичкам советуют ставить Linux и не «париться», это в корне не верный подход, На самом деле все дело в настройках операционной системе на которой вы работаете. В Linux кодировка и отображение кодировки настроено на стандарт unicode utf-8 или unicode utf-32le (смотря какой конкретный дистрибутив) и оба стандарта совместимы, а в Windows исторически сложилось Legacy разработка, настройка кодировки отображения идет в console cp866(Если вы используете русский диструбутив windows) а winapi использует code pages 1251(На русском дистрибутиве) в связи с этим у нас несколько путей решения:

  • Сохранять исходные коды в cp866 чтобы компилятор или интерпретатор «вшивал» код символов для conosle windows по умолчанию.

  • Подменять шрифты где символы соответствуют кодировке cp866 то есть символ «А» в шрифте графически был на месте символа который выглядит как не «А». Например в нумерации кодировки для символа «А» cp866 это код 0x80, а для символа windows code pages 1251 0xc0 таким образом именно в шрифте нужно перерисовать символы где должны отображаться буква «А».

  • Подменять кодировку программно нужно написать небольшую функцию(процедуру) которая перехватывает весь поток вывода и подменяет символы на ту кодировку которая отображает символы по умолчанию это cp866.

  • Самый рациональный и простой метод это переключить операционную систему в нужную вам кодировку, для console windows это будет команда в самой консоле chcp 1251 для кодировки windows code pages 1251, а для unicode utf-8 соответственно chcp 65001 (список всех кодировок windows) для языка C++ можно написать команду после main system("chcp 1251");

Все эти советы актуальны для любой платформы (переключение кодировки самой консоли читайте в документации операционной системы), есть и более хитрые или изящные способы кодирования символов, но в данной статье-заметке кодировки рассмотрены с точки зрения операционных систем.

P. S. По мимо обсуждения статьи в комментариях буду рад, пообщаться в telegram: @almost_human
Поделиться с друзьями
-->

Комментарии (20)


  1. Ruckus
    09.06.2017 16:33

    У меня только один вопрос. Как вместе с кодировкой консоли изменить вывод всего ПО на utf8 и похоронить эту чёртову легаси из IBM866 и cp1251?


    1. Boctopr
      09.06.2017 16:37
      -1

      Это эволюционный процесс, кодировки рождаются мутируют и отмирают, кодировка utf-8 далеко не вершина идеала и эволюции, просто в данный момент времени она более удобна и предпочтительна, однажды кодировка utf-8 точно также будет для следующего поколения программистов legacy.


      1. Ruckus
        09.06.2017 16:47
        -1

        Это не меняет вопроса. Мне нужен весь вывод в одном стандарте, как мне этого добиться?
        Вестимо, что UTF международен и в нём я могу выводить как русские и английские символы, так и японские иероглифы с эмоджи и вообще на сегодняшний день это международный стандарт дефакто, а дальше скорей всего будет развиваться, как вы и сказали, в сторону совместимого UTF32 и подобных.


        1. Ruckus
          09.06.2017 16:53
          -1

          Ох да, спасибо за минус, конечно я неправ, UTF не международный, а в cp1251 я могу вывости японский текст без особых проблем. И, конечно, при этом у меня вывод компилятора в кодировке IBM866 будет так же на русском отображаться без искажений, спасибо что сделали всё это возможным своим минусом.


          1. Boctopr
            09.06.2017 17:03
            -1

            К сожалению Unicode это всего лишь рекомендация к использованию кодировки и это стандарт дефакто, но это не законодательный стандарт, программисты вольны выбирать кодировку которая подходит под задачи программы например где то рационально использовать 7бит на символ. Отобразить японский в кодировке windows code pages 1251 конечно же невозможно, поэтому многие старые программы переключали кодировки во время выполнения


            1. Ruckus
              09.06.2017 17:10
              -1

              Как переключить кодировку консоли в момент исполнения программы и что после смены кодировки консоли будет с предыдущим тестом?


              1. telhin
                09.06.2017 17:15
                +1

                В статье приведен пример system("chcp 1251"); меняет на горячую, предыдущий вывод не портит, консоль не перезапускает.


        1. Boctopr
          09.06.2017 16:59
          -1

          Дело в том что кодировка Unicode также эволюционирует вместе с программным обеспечением на данный момент Unicode версии 9.0.0, например Unicode версии 1.0.0 не отображал многие естественные языки, многие графические знаки и эмодзи.


          1. Ruckus
            09.06.2017 17:08
            -1

            Вы меня совсем не слушаете?
            Windows вот тоже эволюционирует, когда-то был 3.11 без plug&play и UTF, а сейчас уже 10 без UTF (хоть plug&play как-то работает, о качестве не говорю), думаю через пару лет выйдет ещё несколько наборов обновлений, которые будут всё так же ловить BSOD и не поддерживать UTF. Мне нужна консоль с многоязычностью и поддержкой вывода стандартного софта, что мне делать?
            Вместо того, чтобы выдавать философфские фразы не о чём лучше бы сказали если решение и знаете ли вы его.


            1. Boctopr
              09.06.2017 17:13

              В статье написано что console windows возможно переключить в любую кодировку в том числе в unicode, в том числе в utf-7, utf-8, utf-16le,utf-16be, utf-32le, utf-32be, но к сожалению отображать абсолютно все символы unicode она не способна поскольку нет системных шрифтов поддерживающих unicode версии 9.0.0


  1. telhin
    09.06.2017 16:51
    +1

    Была подобная проблема с выводом системных ошибок на Windows формата «Ошибка: файл не найден» <=> «Error: file not found».
    Ошибки форматировались в зависимости от используемой локали, но выбирали cp1251 вместо консольной cp866.

    #ifdef _WIN32
    string GetLastErrorString() {
      DWORD err = GetLastError();
      char* msg_buf;
      FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (char*)&msg_buf,
            0,
            NULL);
      string msg = msg_buf;
      LocalFree(msg_buf);
      return msg;
    }
    
    void Win32Fatal(const char* function) {
      Fatal("%s: %s", function, GetLastErrorString().c_str());
    }
    #endif
    


    Решал через запрос «системной» кодировки через
    GetACP();
    

    А затем последующей установкой в консоли
    SetConsoleCP();
    SetConsoleOutputCP();
    


    Вроде как получилось решение не зависящее от локали пользователя и исправляющее баг в русской локализации cp1251 <=> cp866.


    1. Ruckus
      09.06.2017 16:53
      -1

      А японский сможете? Или хотябы немецкий? Может эмодзи?
      За решение исключительно для русского, конечно, спасибо, но это не спасает.


      1. telhin
        09.06.2017 17:01
        +1

        Это решение для системных ошибок, которые не верно форматируются из-за различия кодовых страниц у win32 api и консоли. Решение работает для всех локалей, у которых присутствует такой баг (кроме русского я не знаю), для остальных ничего не меняет.
        Системные сообщения об ошибках хранятся в так называемых Message Tables и для русского языка они там уже в cp1251. Если хотите унификации и utf8, то начать надо с преобразования этих самых Message Tables, но это вопрос к мелкомягким, а сейчас я представил решение, которое работает на настоящее время.


        1. Ruckus
          09.06.2017 17:13
          -1

          Да я не об ошибках, а о выводе в консоль сообщений Telegram хотябы. Там ники у некоторых на иврите и японском бывают и эмоджи повсюду, так ещё и говорят иногда не на русском. А для отладки часто приходится мониторить реалтайм. Я не могу этого добиться с текущими кодировками, не написав своего GUI?


          1. telhin
            09.06.2017 17:22
            +1

            Нужно в консоль поставить шрифт поддерживающий эмодзи и прочие приблуды, которые требуются, сказать консоли system("chcp 65001"); и спокойно работать в Unicode кодировке.


          1. Boctopr
            09.06.2017 17:22

            Ваша задача более специфична чем просто вывод на консоль, консоль не поддерживает языки где письменность происходит справа налево (арабский и тд), или сверху вниз (японский и тд), а также консоль не обрабатывает управляющие коды


            1. Ruckus
              09.06.2017 17:26

              Мне достаточно видеть символы без кракозябр, что я по умолчанию получаю в других ОС. Я могу назвать это лишь очередной недоработкой.


            1. telhin
              09.06.2017 17:28
              -1

              А можно подробнее про поддержку управляющих кодов unicode. Я думал что это просто начало ASCII таблицы. Есть еще символы управляющие направлением письма?


              1. Boctopr
                09.06.2017 17:32

                Управляющие символы это точно такая же кодировка управлением потока вывода, а не отображения. Почитать можно в разных источниках, на википедию ведет эта ссылка.


  1. tandzan
    09.06.2017 16:54
    +1

    Искал консольный логгер под windows, способный выводить widechar и перенаправляющий сообщения в stdout и stderr, вышла грустная история. Boost.Log выводил иероглифы, при добавлении кодировки пропадали атрибуты-префиксы сообщений и переводы строк (wtf!?). Автор easylogging++ прямо завил о том, что не поддерживает widechar. И т.п. В результате для моей софтины родится такой вот велосипед. Дополнительное еще для ::GetLastError() приходится вызывать CharToOemBuffA.