Проблемы с std::cout в Linux

Модератор: Модераторы разделов

Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Проблемы с std::cout в Linux

Сообщение shau-kote »

Всем доброго времени суток.

Являясь студентом, постоянно пишу на C++. До этого использовал MinGW под Windows, но недавно решил вернуться на Arch, в связи с чем логично стал использовать gcc.

Раньше для вывода я просто использовал std::wcout, вызывая при этом setlocale(LC_ALL, "Russian") в начале программы и предваряя все строковые литералы буквой "L".
Всё прекрасно работало, как вывод русского текста, так и вывод чего-либо ещё.

Но сейчас, на Linux'е, я столкнулся с двумя проблемами:
1) Русский текст выводится вопросительными знаками. При том, что в системе установлена локаль ru_RU.UTF-8 и консоль поддерживает вывод русских букв;
2) По окончанию программы выводится какой-то мусор, выглядящий как символ % на сером фоне. Лечится с помощью cout << endl, но всё равно непонятно, откуда он берётся.

Подскажите, пожалуйста, как решить первую проблему и в чём причина второй?
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21507
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Проблемы с std::cout в Linux

Сообщение Bizdelnick »

shau-kote писал(а):
13.03.2013 18:33
Русский текст выводится вопросительными знаками

Что за текст? Он, часом, не достался в наследство от винды и не пребывает в какой-нибудь cp1251?

shau-kote писал(а):
13.03.2013 18:33
По окончанию программы выводится какой-то мусор, выглядящий как символ % на сером фоне

Аналогично - не остался ли там в наследство от винды какой-нибудь '\r'?
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Re: Проблемы с std::cout в Linux

Сообщение shau-kote »

Никак нет.
Вывожу строковый литерал либо строку считанную со входного потока.
Строковый литерал впечатан в исходный код, который набран в текстовом редакторе в самом Arch'е в UTF-8.
Спасибо сказали:
Аватара пользователя
Женя Подсыпальников
Сообщения: 482

Re: Проблемы с std::cout в Linux

Сообщение Женя Подсыпальников »

// Arch, ru_RU.UTF-8, edited in nano, pocessed in xfce4-terminal :

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

// first.cpp
#include <iostream>

int main()
{
  std::cout << "Программирование\n";
  return 0;
}


$ c++ first.cpp -o first.exe
$ ./first.exe
Программирование
$

:)
Пойдём на рыбалку !
Спасибо сказали:
Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Re: Проблемы с std::cout в Linux

Сообщение shau-kote »

Опа, действительно.
Я писал std::wcout << L"кириллица", а не как Вы. Написал как у Вас - действительно, работает.
Любопытно, Вы не объясните, почему?
Ведь текст всё же в UTF-8, стало быть выводить его нужно в соответствующий поток, нет?

Вообще, это несколько неожиданно для меня, попробовал сейчас следующее:

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

#include <iostream>

int main()
{
    char str[100];
    std::cin >> str;
    std::cout >> str;
    return 0;
}

- работает!
Но разве обычный char пригоден для хранения символов в UTF-8?..
Спасибо сказали:
Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Re: Проблемы с std::cout в Linux

Сообщение shau-kote »

UPD
С "мусором" разобрался, выведя cat'ом исходный текст программы и заметив в конце вывода то же самое. Сменил zsh на дефолтный bash - исчезло.
Очевидно, какая-то ерунда с конфигурацией zsh. :\
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5480
ОС: Gentoo

Re: Проблемы с std::cout в Linux

Сообщение /dev/random »

shau-kote писал(а):
13.03.2013 18:33
...Раньше для вывода я просто использовал std::wcout, вызывая при этом setlocale(LC_ALL, "Russian") в начале программы...

Указывать следует либо корректную локаль (например, "ru_RU.UTF8"), либо пустую строку для автоопределения. Несуществующие локали, вроде вашей, формально приводят к неопределённому поведению, на практике работают как синоним "C".
Правильный код (только ключевые строки):

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

setlocale(LC_ALL, "");
std::wcout << L"Привет, мир!\n";



shau-kote писал(а):
14.03.2013 04:29
Но разве обычный char пригоден для хранения символов в UTF-8?..

Символов - нет. Байтов - да. Если так делать, то один символ может занять больше одного байта. И кстати, не путайте UTF-8 и Unicode.

Строки вида L"" хранят данные в каком-либо внутреннем представлении юникода, обычно UCS2 или UCS4, а wcout перекодирует текст в установленную локалью кодировку (возможно, UTF-8, а возможно, что-то однобайтовое).

Строки вида "" хранят набор байтов (NUL-terminated), в той кодировке, в которой они были введены в исходниках. cout выводит их без перекодирования и не оглядываясь на локаль. Если кодировка исходников и кодировка терминала совпадают, вывод будет правильным. Иначе - нет.

shau-kote писал(а):
14.03.2013 04:45
UPD
С "мусором" разобрался, выведя cat'ом исходный текст программы и заметив в конце вывода то же самое. Сменил zsh на дефолтный bash - исчезло.
Очевидно, какая-то ерунда с конфигурацией zsh. :\

zsh так уведомляет пользователя, что запущенная программа вывела незавершённую строку (без "\n" в конце). Для удобства.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Проблемы с std::cout в Linux

Сообщение NickLion »

Поправлю, что L"" и std::wcout работают с wide-char — wchar_t — для windows это обычно 2 байта и UCS2 (до Win2000 включительно) или UTF-16 (WinXP+), для Linux — 4 байта и UTF-32.
Спасибо сказали:
Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Re: Проблемы с std::cout в Linux

Сообщение shau-kote »

/dev/random, спасибо за подробный комментарий.
/dev/random писал(а):
14.03.2013 09:07
Указывать следует либо корректную локаль (например, "ru_RU.UTF8"), либо пустую строку для автоопределения.

В смысле, в Linux? Или вообще?
Поскольку, когда я компилировал свой код в Windows, я поначалу тоже полагал, что нужно передать что-то вроде "utf-8", а оказалось, что рабочим является именно тот вариант, что я привёл.

И я правильно, понял, что вариант, приведённый Женей, хоть и является рабочим, но, вообще говоря, не является "хорошим", поскольку слабо переносим, да и вообще не гарантирует корректного вывода?
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5480
ОС: Gentoo

Re: Проблемы с std::cout в Linux

Сообщение /dev/random »

shau-kote писал(а):
15.03.2013 20:27
/dev/random писал(а):
14.03.2013 09:07
Указывать следует либо корректную локаль (например, "ru_RU.UTF8"), либо пустую строку для автоопределения.

В смысле, в Linux? Или вообще?
Поскольку, когда я компилировал свой код в Windows, я поначалу тоже полагал, что нужно передать что-то вроде "utf-8", а оказалось, что рабочим является именно тот вариант, что я привёл.

Пример относится к Linux (точнее, к GNU, т.к. ядро тут ни при чём), а всё остальное - к стандарту. Набор локалей в каждой системе может быть свой. Единственный надёжный вариант - использовать автоопределение.

shau-kote писал(а):
15.03.2013 20:27
И я правильно, понял, что вариант, приведённый Женей, хоть и является рабочим, но, вообще говоря, не является "хорошим", поскольку слабо переносим, да и вообще не гарантирует корректного вывода?

Да, он будет работать только если кодировка исходников и кодировка терминала совпадают. Лучше делать так, как я написал. А ещё лучше - использовать что-нибудь вроде gettext для многоязычности (в исходнике всё на английском, а переводы берутся из отдельных файлов). Выбор между "быстро" и "хорошо" - за вами.
Спасибо сказали:
Аватара пользователя
shau-kote
Сообщения: 417
Статус: злобный хоббит
ОС: Arch

Re: Проблемы с std::cout в Linux

Сообщение shau-kote »

/dev/random, спасибо, наконец-то разобрался. (:
Спасибо сказали:
Аватара пользователя
Olej
Сообщения: 659
ОС: Fedora, Mint, Debian, QNX

Re: Проблемы с std::cout в Linux

Сообщение Olej »

shau-kote писал(а):
15.03.2013 20:27
В смысле, в Linux? Или вообще?


Вообще :drinks:

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


shau-kote писал(а):
13.03.2013 18:33
недавно решил вернуться на Arch, в связи с чем логично стал использовать gcc.


"в связи с чем логично использовать" :rolleyes: ... Clang

Спасибо сказали:
Аватара пользователя
Olej
Сообщения: 659
ОС: Fedora, Mint, Debian, QNX

Re: Проблемы с std::cout в Linux

Сообщение Olej »

shau-kote писал(а):
14.03.2013 04:29
Но разве обычный char пригоден для хранения символов в UTF-8?..


"обычный char" замечательно пригоден :rolleyes: для хранения символов UTF-8 ... и не только для хранения, но и для ввода, и для вывода ... и не только UTF-8, а и UTF-16, и UTF-32, если захотите... - обычный char тупо хранит численные значения байт за байтом.

Но обычный char абсолютно непригоден для операций с контекстом строки: поиск, замещение и т.д. ... начиная с простейшей strlen() которая станет врать :console:

И вот тут у вас есть на выбор 2-альтернативы (для хранения):

- использовать "широкие" символы wchar_t, которые являются (по POSIX) прямым изображением UNICODE кодировки UTF-32, и, естественно, каждый символ занимает 4 байта; для них есть все эквиваленты строчных функций: strchr() -> wcschr() и т.д. + операции: wcout << ... , wcin >> ...

- многобайтные представления, которые хоть и загружены в char[], но содержимое представлено именно в кодировке UTF-8 (т.е. тоже UNICODE, но в другой кодировке); они непригодны для непосредственной обработки строковыми функциями, но для них есть функции преобразований (mbtowc(), wctomb(), mblen(), mbrtowc(), wcrtomb(), mbrlen() и т.д.), для преобразования в wchat_t[] и обратно.

Спасибо сказали:
Аватара пользователя
Olej
Сообщения: 659
ОС: Fedora, Mint, Debian, QNX

Re: Проблемы с std::cout в Linux

Сообщение Olej »

shau-kote писал(а):
13.03.2013 18:33
Раньше для вывода я просто использовал std::wcout, вызывая при этом setlocale(LC_ALL, "Russian") в начале программы и предваряя все строковые литералы буквой "L".
Всё прекрасно работало, как вывод русского текста, так и вывод чего-либо ещё.


В Windows так ("Russian") называется локаль. Или, если совсем точно (не укорачивая) эта локаль в Windows записывается как "Russian_Russia.1251".
А KOI-8r, например, как "Russian_Russia.20866" :wacko: (неожиданно, правда? :rolleyes: ).

А в Linux локали называются совсем по другому.
Можете все их посмотреть сами:

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

bash-4.2$ locale -a | grep ru
ru_RU
ru_RU.iso88595
ru_RU.koi8r
ru_RU.utf8
...


Так что явно записывать локаль в коде программы - дело дурное... :rolleyes:
Спасибо сказали: