переносимость Lin<=>Win консольных приложений

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 22 мар 2013, 17:10

Вопрос связан с соседней темой: исполнение Windows промышленных проектов под Wine.

Состоит он вот в чём:
- создадим (напишем) платформенно независимое консольное приложение ... "Привет мир!" :lol:
- на С или С++ ... используя либо Boost (как в теме откуда переплыл вопрос), или Apache Portable Runtime (APR) ... , или любой инструмент переносимого программирования - проблема будет наблюдаться везде!
- но в программе будут выводиться русскоязычные строки:

Код: Выделить всё

std::cout <<  "Привет мир!" << std::endl;
- такая программа в Linux будет нормально (ожидаемо) исполняться, т.к. в современном Linux и редакторы кода (или IDE разработки) и терминал работают в UTF-8 (Unicode);
- если перенести такое приложение в MS Visual Studio (а для чего мы тогда уродовались с Boost? ;-) ) + добиться нормальной сборки проекта (указанием путей инклудов и бинарных библиотек Boost), то программа может быть скомпилирована...
- но при запуске её в консоли - получим кракозябры ... текст UTF-8, который прекрасно видится как надо в редакторе MS Visual Studio, при выводе в тупую Windows консоль, ожидающую свою уродливую кодировку CP-1251, отображается бредовым набором символов.

Как?

Проблема здесь не в переносимости UNIX <-> Windows, проблема здесь чисто Windows, потому как если такое приложение "Привет мир!" набить в пустом консольном проекте Windows, то получится такое же уродство. Но меня мало занимают проблемы разбирательства с Windows-уродствами ... пусть они "сами хоронят своих мертвецов".

Вопрос маленький и конкретный, состоящий в том: как запустить консольное приложение в Windows с отображением русского текста?
И с тем, чтобы явно в код не вписывать всякие Windows-ные штучки-дрючки, по типу явного преобразования строк в CP-1251 ... что не будет работать ни в одной другой системе.

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 22 мар 2013, 17:31

Olej писал(а): Проблема здесь не в переносимости UNIX <-> Windows, проблема здесь чисто Windows, потому как если такое приложение "Привет мир!" набить в пустом консольном проекте Windows, то получится такое же уродство. Но меня мало занимают проблемы разбирательства с Windows-уродствами ... пусть они "сами хоронят своих мертвецов".

Вопрос маленький и конкретный, состоящий в том: как запустить консольное приложение в Windows с отображением русского текста?
Вот такой "родной" (никто никуда не переносился) проект VisualStudio, свеже набранный - ruscons1, исходный код ruscons1.cpp выглядит так:

Код: Выделить всё

#include "stdafx.h"

#include <iostream>
using namespace std;

int _tmain( int argc, _TCHAR* argv[] ) {
	cout << "Выводится русский текст" << endl;
	return 0;
}
А результат его выполнения в консоли выглядит так :twisted: :

Код: Выделить всё

C:\Documents and Settings\olej\Мои документы\Visual Studio 2010\Projects\ruscons1\Debug>ruscons1.exe
┬√тюфшЄё  Ёєёёъшщ ЄхъёЄ

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 22 мар 2013, 20:47

Что они начудили там в Windows с локализациями я понять не могу!

Делаем проект такой (для проверки локализаций):

Код: Выделить всё

#include <iostream>
using namespace std;

int main( int argc, char* argv[] ) {
	cout << "russian string: Выводится русский текст" << endl;
	// locale::global( locale( "" ) );
	cout << "russian string: После глобальных системных установок" << endl;
	cout << "old locale: " << cout.getloc().name().c_str() << endl;
	while( true ) {
		char s[ 40 ];
		cout << "new locale: " << flush;
		cin >> s;
		locale loc;
		try { loc = locale( (char*)s ); }
		catch( runtime_error ) {
			cout << "wrong locale" << endl;
			continue;
		}
		// cout.imbue( loc ); // это не работает!
		locale::global( loc );
		cout << "russian string: Выводится русский текст" << endl;
	}
	return 0;
}
Результатов его я не понимаю:

Код: Выделить всё

C:\Documents and Settings\olej\Мои документы\Visual Studio 2010\Projects\ruscons1\Debug>ruscons1.exe
russian string: ┬√тюфшЄё  Ёєёёъшщ ЄхъёЄ
russian string: ╧юёых уыюсры№э√ї ёшёЄхьэ√ї єёЄрэютюъ
old locale: C
new locale: POSIX
wrong locale
new locale: en_US
wrong locale
new locale: ru_RU
wrong locale
new locale: English_USA.1252
russian string: Выводится русский текст
new locale: Russian
russian string: Выводится русский текст
new locale: Ukrainian
russian string: Выводится русский текст
new locale: German
russian string: Выводится русский текст
new locale:
^C
Человеческих локалей: POSIX, en_US, ru_RU - у них нет, есть какие-то уродства: English_USA.1252 и т.д.

Но вот той строчки закомментированной, похоже, достаточно для установки дефаултной локали, которая у меня в системе русская:

Код: Выделить всё

	locale::global( locale( "" ) );
Можно, конечно, использовать Boost систему локализации: Using Boost.Locale, но это пока никак не есть предметом моих интересов ;-)

cema
Писатель
Сообщения: 246
Зарегистрирован: 14 июн 2012, 06:01
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение cema » 22 мар 2013, 21:56

Может не в тему, просто увидел знакомые буквы...
Я когда-то решил как-то так http://goodluck.uzelok.net/archives/55
Если что это тоже мой ресурс :-)

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 00:24

cema писал(а):Может не в тему, просто увидел знакомые буквы...
Я когда-то решил как-то так http://goodluck.uzelok.net/archives/55
Там:
Первый способ заключается в том, что бы после написания программы сохранить файлы в кодировке OEM. Сделать это можно в блокноте, например Bred или Notepad 2. Просто открываете файл и сохраняете в DOS кодировке.
Второй способ - это воспользоваться файлами CyrIOS.cpp и CyrIOS.h, скачать их можно тут.
Просто добавьте эти файлы в проект и в файле проекта допишите:
#include “CyrIOS.h”
Это то, что будет работать в Windows, но моментально перестанет работать при перенесении кода без внесения изменений в Linux (или ещё куда-то). 1-й способ для этого совсем не уместный, а 2-й ... посмотрю что там позже. ;-)

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 10:44

Olej писал(а):Это то, что будет работать в Windows, но моментально перестанет работать при перенесении кода без внесения изменений в Linux (или ещё куда-то).
Посмотрел мельком что делают Windows-программисты в этом случае - это полный бред!: они начинают преобразовывать строку перед выводом используя ChatToOem() ... а то и вообще ковыряя вручную коды символов :-o

Кое-что о причине того ужаса, который происходит с кодировками в Windows, описано здесь: Кириллица в консоли... хотя сама статья полна ошибок, хоть это и сайт "Учебники":
toefl, ielts, gre, gmat Унікальна методика підготовки
... действительно "уникальная" :-o
Вот как бывает, если смотреть на природу вещей с "Windows колокольни" :lol:

Удивляет то, что состояние дел этих в Windows противоречит такими стандартными понятиями языка С++ как: локаль, локализация, locale, ...

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 13:28

Программа, которая позволяет хоть как-то разобраться в этом идиотизме + просмотреть как в Windows записываются имена (символьные) локалей (которые у них пишутся совершенно не как у людей):

Код: Выделить всё

#include <clocale>
#include <iostream>

using namespace std;

int main( int argc, char* argv[] ) {
	cout << "russian string: Выводится русский текст" << endl;
	cout << "default locale: " << locale( "" ).name().c_str() << endl;
	cout << "old cout.locale: " << cout.getloc().name().c_str() << endl;
	while( true ) {
		char s[ 40 ];
		cout << "new locale: " << flush;
		cin >> s;
		locale loc;
		try { loc = locale( (char*)s ); }
		catch( runtime_error ) {
			cout << "wrong locale" << endl;
			continue;
		}
		cout.imbue( loc ); 
		cout << "cout.locale : " << cout.getloc().name().c_str() << endl;
		locale::global( loc );
		cout << "locale : " << std::setlocale( LC_ALL, NULL ) << endl;
		cout << "russian string: Выводится русский текст" << endl;
	}
	return 0;
}
Её выполнение:

Код: Выделить всё

russian string: ┬√тюфшЄё  Ёєёёъшщ ЄхъёЄ
default locale: Russian_Russia.1251
old cout.locale: C
new locale: Russian
cout.locale : Russian_Russia.1251
locale : Russian_Russia.1251
russian string: Выводится русский текст
new locale: Russian_Russia.65001
wrong locale
new locale: Russian_Russia.28595
cout.locale : Russian_Russia.28595
locale : Russian_Russia.28595
russian string: Т?тюфш?ё? №?ёёъшщ ?хъё?
new locale: Russian_Russia.20866
cout.locale : Russian_Russia.20866
locale : Russian_Russia.20866
russian string: бШБНДХРЯЪ ПСЯЯЙХИ РЕЙЯР
new locale: C
cout.locale : C
locale : C
russian string: ┬√тюфшЄё  Ёєёёъшщ ЄхъёЄ
new locale:
Откуда берётся вот этот идиотизм в записи? типа: Russian_Russia.20866
А это - KOI-8r :-o
Не ожидали? :lol:
А вот где в Windows мы его находим:
locale.GIF
locale.GIF (55.35 КБ) 8505 просмотров

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 13:56

Olej писал(а): Откуда берётся вот этот идиотизм в записи? типа: Russian_Russia.20866
А это - KOI-8r :-o
Не ожидали? :lol:
А вот где в Windows мы его находим:
1. Я проделал это всё в Windows XP, но, предполагаю, что всякие прочие: WIndows 7, WIndows 8 (9, 10 и 11 :lol: ) - недалеко ушли! :evil:

2. Картина, в общем, понятная:

- вызовом вида loc = locale( "Russian_Russia.20866" ) можно создать новую локаль (переменную)...

- дальше можете или установить эту локаль для своей программы целиком ( locale::global( loc ) ), или для потока вывода ( cout.imbue( loc ) ) ... или для ввода и т.д.

- в Windows имена локалей (текстовая строка!) называются совершенно по-идиотски ... в отличие от всех нормальных людей ;-) - "Russian_Russia.20866" - это русская кодировка KOI-8r ...

- а у нормальных людей (в UNIX, в описаниях С++ у Б.Страуструпа, POSIX / IEEE Std 1003.1-2001) эти локали именуются по типу: "en_US.UTF-8", "de_DE.UTF-8", "ru_RU. ISO-8859-5"

Код: Выделить всё

bash-4.2$ set | grep LANG
LANG=ru_RU.UTF-8

Код: Выделить всё

bash-4.2$ locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
См.: man (1P) locale, man (1P) localedef


- для того, чтобы в консоль Windows шёл нормальный русский вывод, нужно установить:

Код: Выделить всё

locale::global( locale( "Russian_Russia.1251" ) );
или

Код: Выделить всё

cout.imbue( locale( "Russian_Russia.1251" ) );
или, если это С, а не С++ :

Код: Выделить всё

setlocale( LC_ALL, "Russian_Russia.1251" )
Это плохо, потому, что это сработает в Windows, но не будет работать при переносе в UNIX.

- но если операционная система локализована, русскоязычная (установлены переменные окружения?), то достаточно и там и там (Linux, Windows) установить для программы локализацию системы (дефаултную):

Код: Выделить всё

locale::global( locale( "" ) );
Вот это к вопросу переносимости консольных программ между Linux и Windows, похоже, проблему полностью решает.

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 14:11

Olej писал(а): - а у нормальных людей (в UNIX, в описаниях С++ у Б.Страуструпа, POSIX / IEEE Std 1003.1-2001) эти локали именуются по типу: "en_US.UTF-8", "de_DE.UTF-8", "ru_RU. ISO-8859-5"

Код: Выделить всё

bash-4.2$ set | grep LANG
LANG=ru_RU.UTF-8
Кстати, интересно, вот что происходит, если в Linux изменить дефаултные установки языка, которые потянут и изменение locale:

Код: Выделить всё

bash-4.2$ export LANG=ru_RU.1251

bash-4.2$ set | grep LANG
LANG=ru_RU.1251

bash-4.2$ locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=ru_RU.1251
LC_CTYPE="ru_RU.1251"
LC_NUMERIC="ru_RU.1251"
LC_TIME="ru_RU.1251"
LC_COLLATE="ru_RU.1251"
LC_MONETARY="ru_RU.1251"
LC_MESSAGES="ru_RU.1251"
LC_PAPER="ru_RU.1251"
LC_NAME="ru_RU.1251"
LC_ADDRESS="ru_RU.1251"
LC_TELEPHONE="ru_RU.1251"
LC_MEASUREMENT="ru_RU.1251"
LC_IDENTIFICATION="ru_RU.1251"
LC_ALL=

bash-4.2$ ./randwr
terminate called after throwing an instance of 'std::runtime_error'
  what():  locale::facet::_S_create_c_locale name not valid
Аварийный останов (core dumped)

Код: Выделить всё

bash-4.2$ export LANG=ru_RU.UTF-8

bash-4.2$ ./randwr
Запуск:  ./randwr файл [число записей] [размер блока записи[{K,M}]]

Аватара пользователя
Olej
Писатель
Сообщения: 13937
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: переносимость Lin<=>Win консольных приложений

Непрочитанное сообщение Olej » 23 мар 2013, 17:02

Olej писал(а): - но если операционная система локализована, русскоязычная (установлены переменные окружения?), то достаточно и там и там (Linux, Windows) установить для программы локализацию системы (дефаултную):

Код: Выделить всё

locale::global( locale( "" ) );
Интересно, что в качестве имени языка/локали можно в переменных окружения записать любую ерунду:

Код: Выделить всё

bash-4.2$ export LANG=ru_RU.zzz

Это, естественно, в Linux + bash ... для которых ru_RU.1251 точно такая же ерунда, как и ru_RU.zzz.

И local подхватит это определение:

Код: Выделить всё

bash-4.2$ locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=ru_RU.zzz
LC_CTYPE="ru_RU.zzz"
LC_NUMERIC="ru_RU.zzz"
LC_TIME="ru_RU.zzz"
LC_COLLATE="ru_RU.zzz"
LC_MONETARY="ru_RU.zzz"
LC_MESSAGES="ru_RU.zzz"
LC_PAPER="ru_RU.zzz"
LC_NAME="ru_RU.zzz"
LC_ADDRESS="ru_RU.zzz"
LC_TELEPHONE="ru_RU.zzz"
LC_MEASUREMENT="ru_RU.zzz"
LC_IDENTIFICATION="ru_RU.zzz"
LC_ALL=
Но если подставить эти имена (как дефаулты) в определения локали программы, то возбудится исключение - нет такой локали:

Код: Выделить всё

bash-4.2$ ./randwr
terminate called after throwing an instance of 'std::runtime_error'
  what():  locale::facet::_S_create_c_locale name not valid
Аварийный останов (core dumped)

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость