LinuxForum.Fans
Тема

Кириллические кодировки в терминале это возможно?

Автор: hardorsoft 30 сообщений 7995 просмотров
#118205

Здравствуйте! пишу первые программы на Си и столкнулся с такой проблемой -
при использовании библиотеки ncurses терминал упорно не хочет отображать
кириллические символы так как надо.
Пример кода.

text

/*
Программа печатает стихотворение.
*/
#include <stdio.h>
#include <ncurses.h>
#include <locale.h>
int main ()
{
    setlocale(LC_CTYPE, "ru_RU.UTF8");
    initscr();
    start_color();
    init_pair(1, COLOR_RED, COLOR_BLUE);
    attron(COLOR_PAIR(1));
    move(10,30);
    printw("Буря мглою небо кроет, \n\r");
    move(11,30);
    printw("Вихри снежные крутя. \n\r");
    move(12,30);
    printw("То как зверь она завоет, \n\r");
    move(13,30);
    printw("То заплачет, как дитя.\n\r");
    move(14,30);
    printw("          А.С. Пушкин. \n\r");
    getch();
    endwin();
}

На экране появляется приблизительно следующее.

�~Q�~C�~@�~O мгло�~N небо к�~@ое�~B
�~Rи�~E�~@и �~Aнежн�~Kе к�~@�~C�~B�~O.
То как зве�~@�~L она завое�~B,
То запла�~Gе�~B, как ди�~B�~O.
�~P.С. �~_�~C�~Hкин

В книге Гриффитса А. Gcc - компилятор GNU нашел такой подход:

text

#include <locale.h>
#include <libintl.h>

#define PACKAGE "stalker" #define LOCALEDIR "/usr/share/locale"

int main() { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKEGE);

printf("%s\n", gettext("This string will translate.")); }

Но тогда не понятно какой должен быть пакет (PACKAGE)?
В директорию заглядывал нашел там только модули кириллизации для отдельных программ.
Скажу только что терминал работает по умолчанию в кодировке UTF-8.
Пробовал менять кодировку терминала все безрезультатно.
В чем проблема?
Как сделать чтобы кириллические символы отображались правильно?

#118224

Я не знаю как это должно быть правильно (я не обладаю монопольным правом на истину), но я делаю это за два следующих шага.

1.
Нужно поправить файл /etc/default/console-setup. Например, команда, которая выполняется в Ubuntu:
[console]sudo nano /etc/default/console-setup[/console]
После окончания редактирования нажимайте Ctrl-O -- для сохранения, Ctrl-X -- для выхода.

Параметры в файле должны быть следующими:

text

VERBOSE_OUTPUT=no
ACTIVE_CONSOLES="/dev/tty[1-6]"
CHARMAP="UTF-8"
CODESET="CyrSlav"
FONTFACE="Terminus"
FONTSIZE="12x6"
XKBMODEL="pc105"
XKBLAYOUT="us,ru"
XKBVARIANT=","
XKBOPTIONS="grp:ctrl_shift_toggle,grp_led:scroll"

Можно запустить утилиту конфигурации
[console]
sudo dpkg-reconfigure console-setup
[/console]
и в процессе диалога указать тип шрифта, согласно вышеприведенному.


2.
Чтобы при каждой загрузке новая конфигурация учитывалась системой необходимо в конец файла /etc/rc.local добавить команду setupcon:

text

...
setupcon
exit 0

Теперь что касается Вашей программы.
1. В обоих Ваших прогах функция main дожна возвращать какое-то значение. Допишите, например

text

  ...
  return EXIT_SUCCESS;
}

по рабоче-крестьянски return 0;

2.
Как Вы производите компиляцию проги?
Если Вы используете ncurses и используете кириллицу (т.е. широкие символы), то вам нужно указывать линкуемую библиотеку не ncurses, а ncursesw . Обратите внимание на букву w в конце. Это принципиально отличает библиотеки.
Попробуйте так
[console]
$ gcc -Wall -Wextra -pedantic myproga.c -lncursesw
[/console]

#118228

куда-нибудь в раздел "бубунта" перенесите.
в большинстве дистрибутивов такой проблемы просто нет. потому лично я не в курсе, как её решать.

#118245

Достаточный пример:

text
#include <locale.h>
#include <curses.h>

int main() { setlocale(LC_CTYPE,"ru_RU.UTF-8"); /* setlocale(LC_ALL,""); */ initscr();

move(10,30); printw("Буря мглою небо кроет,\n");

getch(); endwin(); return 0; }

[console]$ gcc test.c -l ncursesw
$ ./a.out[/console]

См. man ncurses.

Примечания:
1. В Slackware русская utf8 локаль зовётся ru_RU.UTF-8, но ru_RU.UTF8 и ru_RU.utf8 в качестве параметра setlocale() тоже работают как следует.
2. Вывод "�~Q�~C�~@�~O мгло�~N небо к�~@ое�~B" соответствует тексту в utf8 и локали "C" ==> setlocale() в топикстарте не отработала, как это было задумано ==> ошибка в параметрах?
3. При линковке с ncurses вместо ncursesw в локали ru_RU.UTF-8 получается иной вывод, а при локали "C" получается вывод "�~Q�~C�~@�~O мгло�~N небо к�~@ое�~B" ==> вне зависимости от использованной библиотеки вызов setlocale() не изменил локаль процесса ==> параметры вызова были неверны.
4. Вызов setlocale(LC_ALL,"") устанавливает локаль родительского процесса для текущего.
5. Вызов textdomain() используется для поддержки нескольких локалей при помощи gettext().

#118285

почему вы не желаете проверять возвращаемое значение?

RETURN VALUE
A successful call to setlocale() returns an opaque string that corresponds to the locale set. This string
may be allocated in static storage. The string returned is such that a subsequent call with that string
and its associated category will restore that part of the process's locale. The return value is NULL if
the request cannot be honored.

у вас вообще локаль установилась?

#118286

Все что описано выше попробовал.
Результат ноль.
В случае
[terminal]
gcc test.c -l ncursesw
[terminal]


консоль выводит ошибок на два экрана. Страшно даже здесь их показывать. :-(
то есть наверное библиотека должна быть все-таки ncurses. (?)
cursesw.h по видимому отвечает за многооконные приложения, мне пока нужно нормально сделать одно окно.

i = setlocale();
i = 158371816 - то есть мусор. :-(

Простите, но я кажется просто не знаю как линковать библиотеку locale.h.?!

#118288
hardorsoft написал:
консоль выводит ошибок на два экрана. Страшно даже здесь их показывать. :-(

ну уберите под SPOILER
телепаты в отпуске.

hardorsoft написал:
Все что описано выше попробовал.
Результат ноль.

т.е. setlocale() возвращает NULL?

#118289
hardorsoft написал:

1.
В случае
gcc test.c -l ncursesw
консоль выводит ошибок на два экрана. Страшно даже здесь их показывать. :-(

между "-l" и "ncursesw" пробела не должно быть! Все пишется слитно: -lncursesw

2.
Возможно у Вас еще и библиотека не установлена. Проверьте, и если ее нет, установите
[console]
$ sudo apt-get install libncursesw5-dev
[/console]
опять же обратите внимание на буквочку w перед пятеркой.

3.
Насчет инклуда, (я точно не помню, а искать как обычно -- лень), помоему можно указывать просто ncurses.h, без всяких w.

4. Да и скажите, какой у Вас дистрибутив.

#118294

Да слитно но матерится компилятор не слабо.

setlocale возвращает мусор.
библиотеку установил libncursesw5-dev
Результат теперь лучше в терминале только одна ошибка и то мелкая ее я исправил и она к делу не относится.

Дистрибутив не совсем мой, но Linux Mint 7 (Gloriya).

Библиотеку locale.h линковать не нужно по видимому?
Я прав?

#118303

hardorsoft написал:
setlocale возвращает мусор.
Она возвращает строку (точнее, указатель на строку с завершающим \0), а уж как мусор её интерпретировали самостоятельно.

Неужели прототип в man setlocale ( char *setlocale(int category, const char *locale) ) ни о чём не говорит?

char * loc;
...
if ( NULL!=(loc=setlocale(LC_CTYPE,"ru_RU.UTF-8") ) printf(loc);
else printf("* Error: setlocale()\n");
#118306

Да вы правы я не обратил внимание по неопытности что setlocale возвращает массив.
Нет простите но setlocale возвращает ошибку.
изменилось только то что установил libncursesw5-dev
Проблемы с кодировками остались.
Сути дела это не меняет.
Нужно ли locale.h компилировать c каким-то параметром -ln locale(?)

#118308
hardorsoft написал:
setlocale возвращает мусор.

мусор в чьей-то голове. :(
а в документации ясно сказано:

RETURN VALUE
A successful call to setlocale() returns an opaque string that corresponds to the locale set. This string
may be allocated in static storage. The string returned is such that a subsequent call with that string
and its associated category will restore that part of the process's locale. The return value is NULL if
the request cannot be honored.

может перевести?
впрочем спасибо bormantу, уже перевели...

#118312

hardorsoft,
testlocale.c

text

#include <locale.h>
#include <curses.h>

char *loc;

int main() { loc=setlocale(LC_CTYPE,"ru_RU.UTF-8"); /* setlocale(LC_ALL,""); */ initscr();

move(9, 30); printw(NULL==loc?"Error: locale()":loc); move(10,30); printw("Буря мглою небо кроет,\n");

getch(); endwin(); return 0; }

[console]$ gcc testlocale.c -lncursesw && ./a.out[/console]
Что наблюдаем? А после замены на loc=setlocale(LC_ALL,""); ?

#118315

The return value is NULL if the request cannot be honored.
Если функция возвращает Null то запрос не удалось выполнить.
Я правильно понимаю?
Наверное
if ( NULL==(loc=setlocale(LC_CTYPE,"ru_RU.UTF-8") ) printf(loc);
else printf("* Error: setlocale()\n");
Нужно переписать

if ( NULL==(loc=setlocale(LC_CTYPE,"ru_RU.UTF-8") ) printf("* Error: setlocale()\n");
else printf(loc);

Переписал кодировка RU_ru.UTF8.


То есть я все делал правильно, вы просто проверяли меня не робот ли я? :-0)

Откомпилировал со страшной буквой w.
Проблемы с кодировками остались.

loc=setlocale(LC_ALL,""); (?)
те же результаты.

#118317
hardorsoft написал:
Библиотеку locale.h линковать не нужно по видимому?
Я прав?

locale.h это не библиотека.

Вам нужно слинковать ncurses, а о том как это делать, сказано в ВАШЕМ man ncurses
в моём так:

A program using these routines must be linked with the -lncurses option, or (if it has been generated) with
the debugging library -lncurses_g. (Your system integrator may also have installed these libraries under
the names -lcurses and -lcurses_g.) The ncurses_g library generates trace logs (in a file called 'trace'
in the current directory) that describe curses actions. See also the section on ALTERNATE CONFIGURATIONS.

вот zhevak делает по другому. вам видимо надо делать иначе.

Ещё в манах сказано:

NOTES
Linux (that is, glibc) supports the portable locales "C" and "POSIX". In the good old days there used to
be support for the European Latin-1 "ISO-8859-1" locale (e.g., in libc-4.5.21 and libc-4.6.27), and the
Russian "KOI-8" (more precisely, "koi-8r") locale (e.g., in libc-4.6.27), so that having an environment
variable LC_CTYPE=ISO-8859-1 sufficed to make isprint(3) return the right answer. These days non-English
speaking Europeans have to work a bit harder, and must install actual locale files.

что в переводе на русский значит, что вам ещё нужно установить актуальные для вашей локали файлы. Хотя если setlocale выдаёт не NULL, значит нужные файлы установлены.

#118319
hardorsoft написал:
Нужно переписать

не нужно.
если NULL, то ошибка.

hardorsoft написал:
То есть я все делал правильно, вы просто проверяли меня не робот ли я?

нет. просто у всех работает, кроме вас.

#118321
hardorsoft написал:
loc=setlocale(LC_ALL,""); (?)
те же результаты.

значит у вас не устанавливается локаль. В программах вроде mc русский язык нормально работает? да/нет?

#118324

hardnsoft,
вернитесь к сообщению #13.
А по поводу перепутанного, угу, сначала написал по привычке if ( (loc=setlocale() ), потом вспомнил про возможность NULL!=0, потом добавил проверку NULL== вместо NULL!=, вобщем всё как обычно, когда пытаться делать несколько дел одновременно, и ещё на форум что-то писать wink
У меня по сообщению #13 картинка такая (в обоих случаях):
[console]ru_RU.UTF-8
Буря мглою небо кроет,[/console]
и вывод команды locale соответствующий (ru_RU.UTF-8 для всех LC_ за малым исключением).

#118327

Вам нужно слинковать ncurses, а о том как это делать, сказано в ВАШЕМ man ncurses

что в переводе на русский значит, что вам ещё нужно установить актуальные для вашей локали файлы. Хотя если setlocale выдаёт не NULL, значит нужные файлы установлены.

Проверил еще раз все верно.
И кодировка вроде правильная.
Я вообще пробовал вывести на печать всю таблицу в кодах /001 /002 ... ./255
латиница отображается верно, а в кириллице что то не могу сказать.
Причем если не использовать initscr();
то все отлично с кириллицей.

#118331
hardorsoft написал:
Причем если не использовать initscr(); то все отлично с кириллицей.

Верный признак линковки не с ncursesw.

hardorsoft написал:
Я вообще пробовал вывести на печать всю таблицу в кодах /001 /002 ... ./255
Вы уж там у себя как-то разберитесь, однобайтные кодировки используете или utf8.
Войдите или зарегистрируйтесь, чтобы ответить.