СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Кодировка символов и Console в черновиках Tutorial

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

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


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

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

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


В современном мире, почти все кодировки и отображение контролирует операционная система, чаще всего в 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");

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

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

+1
Ruckus ,  
У меня только один вопрос. Как вместе с кодировкой консоли изменить вывод всего ПО на utf8 и похоронить эту чёртову легаси из IBM866 и cp1251?
–1
Boctopr ,   * (был изменён)
Это эволюционный процесс, кодировки рождаются мутируют и отмирают, кодировка utf-8 далеко не вершина идеала и эволюции, просто в данный момент времени она более удобна и предпочтительна, однажды кодировка utf-8 точно также будет для следующего поколения программистов legacy.
0
+1 –1
Ruckus ,  
Это не меняет вопроса. Мне нужен весь вывод в одном стандарте, как мне этого добиться?
Вестимо, что UTF международен и в нём я могу выводить как русские и английские символы, так и японские иероглифы с эмоджи и вообще на сегодняшний день это международный стандарт дефакто, а дальше скорей всего будет развиваться, как вы и сказали, в сторону совместимого UTF32 и подобных.
0
+1 –1
Ruckus ,   * (был изменён)
Ох да, спасибо за минус, конечно я неправ, UTF не международный, а в cp1251 я могу вывости японский текст без особых проблем. И, конечно, при этом у меня вывод компилятора в кодировке IBM866 будет так же на русском отображаться без искажений, спасибо что сделали всё это возможным своим минусом.
–2
Boctopr ,  
К сожалению Unicode это всего лишь рекомендация к использованию кодировки и это стандарт дефакто, но это не законодательный стандарт, программисты вольны выбирать кодировку которая подходит под задачи программы например где то рационально использовать 7бит на символ. Отобразить японский в кодировке windows code pages 1251 конечно же невозможно, поэтому многие старые программы переключали кодировки во время выполнения
0
+1 –1
Ruckus ,  
Как переключить кодировку консоли в момент исполнения программы и что после смены кодировки консоли будет с предыдущим тестом?
+1
telhin ,  

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

–2
Boctopr ,  
Дело в том что кодировка Unicode также эволюционирует вместе с программным обеспечением на данный момент Unicode версии 9.0.0, например Unicode версии 1.0.0 не отображал многие естественные языки, многие графические знаки и эмодзи.
–1
Ruckus ,  
Вы меня совсем не слушаете?
Windows вот тоже эволюционирует, когда-то был 3.11 без plug&play и UTF, а сейчас уже 10 без UTF (хоть plug&play как-то работает, о качестве не говорю), думаю через пару лет выйдет ещё несколько наборов обновлений, которые будут всё так же ловить BSOD и не поддерживать UTF. Мне нужна консоль с многоязычностью и поддержкой вывода стандартного софта, что мне делать?
Вместо того, чтобы выдавать философфские фразы не о чём лучше бы сказали если решение и знаете ли вы его.
0
Boctopr ,   * (был изменён)
В статье написано что console windows возможно переключить в любую кодировку в том числе в unicode, в том числе в utf-7, utf-8, utf-16le,utf-16be, utf-32le, utf-32be, но к сожалению отображать абсолютно все символы unicode она не способна поскольку нет системных шрифтов поддерживающих unicode версии 9.0.0
+1
telhin ,  
Была подобная проблема с выводом системных ошибок на 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 ,  
А японский сможете? Или хотябы немецкий? Может эмодзи?
За решение исключительно для русского, конечно, спасибо, но это не спасает.
+1
telhin ,  
Это решение для системных ошибок, которые не верно форматируются из-за различия кодовых страниц у win32 api и консоли. Решение работает для всех локалей, у которых присутствует такой баг (кроме русского я не знаю), для остальных ничего не меняет.
Системные сообщения об ошибках хранятся в так называемых Message Tables и для русского языка они там уже в cp1251. Если хотите унификации и utf8, то начать надо с преобразования этих самых Message Tables, но это вопрос к мелкомягким, а сейчас я представил решение, которое работает на настоящее время.
–1
Ruckus ,  
Да я не об ошибках, а о выводе в консоль сообщений Telegram хотябы. Там ники у некоторых на иврите и японском бывают и эмоджи повсюду, так ещё и говорят иногда не на русском. А для отладки часто приходится мониторить реалтайм. Я не могу этого добиться с текущими кодировками, не написав своего GUI?
+1
telhin ,  

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

0
Boctopr ,   * (был изменён)
Ваша задача более специфична чем просто вывод на консоль, консоль не поддерживает языки где письменность происходит справа налево (арабский и тд), или сверху вниз (японский и тд), а также консоль не обрабатывает управляющие коды
0
Ruckus ,  
Мне достаточно видеть символы без кракозябр, что я по умолчанию получаю в других ОС. Я могу назвать это лишь очередной недоработкой.
–1
telhin ,  

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

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