LinuxForum.Fans
Тема

Проблема ввода символов в UTF-8 кодировке при использовании ncurses

Автор: hardorsoft 22 сообщений 6007 просмотров
#132483

Доброго времени суток!
Пожалуйста объясните, что делать. Решаю домашнее задание. Вот код:

text

/*
  Программа определяет гласная или согласная буква была введена
  с клавиатуры.
*/
#include <locale.h>
#include <curses.h>
#include <stdio.h>

#define YES 1 #define NO 0

glasn(ch) char ch; { static char gl[] = "АаЕеИиОоУуЫыЭэЮюЯя\0"; int i;

while(gl[i] && (gl[i] != ch)) i++; if(gl[i]) return(1); else return(0); }

main() { char ch; char *log; int flag; log = setlocale(LC_CTYPE,"ru_RU.UTF-8"); initscr(); printw("*** Проверяем гласная или согласная буква введена с клавиатуры. ***\n"); printw("Введите букву.\n --> "); scanw("%c", &ch); flag = glasn(ch); if(flag == YES) printw("%c - буква гласная.\n", ch); else printw("%c - буква согласная.\n", ch); printw("Нажмите <Enter> для выхода из программы"); getch(); endwin(); return 0; }

Программа компилируется нормально, но вот проблема после того как ввожу символ с клавиатуры
программа выдает ошибку.

[console]
proger$ ./193
Ошибка сегментирования
[/console]

Понятно что в функция scanw("%c", &ch) не может принять символ с номером больше 255.
А в кодировке UTF-8 естественно номер символа выходит за эти пределы.
Вопрос как обойти эти ограничения для данного типа?

#132509
hardorsoft написал:
онятно что в функция scanw("%c", &ch) не может принять символ с номером больше 255.

почему нельзя использовать целое число? оно записывается как %d. Хотя я-бы в данном случае читал-бы байты функцией getch (тоже есть в curses)

#132514

Скорее всего Вы подключаете библиотеку ncurses. Но при использовании широких символов библиотека должна быть другая -- ncursesw

Если не установлена, то сначала
[console]
$ sudo apt-get install libncursesw5-lib
[/console]

Инклуд <ncurses.h> оставьте как есть. Он не повлияет.

(Посоветовать-то я посоветовал :) но сам еще не попробовал. Сейчас сам попробую.)

#132516

Ой! Ёпрст! Глянул на текст Вашей программы! Сейчас Вам напишу. Ждите!

#132615

Библиотека установлена.
У меня как всегда все упирается в проблемы с кодировками.
Программа не совсем моя... Но я подожду.

#132627
zhevak написал:
Скорее всего Вы подключаете библиотеку ncurses. Но при использовании широких символов библиотека должна быть другая -- ncursesw

Если не установлена, то сначала
[console]
$ sudo apt-get install libncursesw5-lib
[/console]

Инклуд <ncurses.h> оставьте как есть. Он не повлияет.

(Посоветовать-то я посоветовал :) но сам еще не попробовал. Сейчас сам попробую.)

А не следует ли вместо ncurses.h включить

text
#include <ncursesw/ncurses.h>

Пётр.

#132636

у вас ман сломался?
вот:

Показать
curs_getch(3X) curs_getch(3X)

NAME
getch, wgetch, mvgetch, mvwgetch, ungetch, has_key - get (or push back) characters from curses terminal
keyboard

SYNOPSIS
#include <curses.h>

int getch(void);
int wgetch(WINDOW *win);
int mvgetch(int y, int x);
int mvwgetch(WINDOW *win, int y, int x);
int ungetch(int ch);
int has_key(int ch);

DESCRIPTION
The getch, wgetch, mvgetch and mvwgetch, routines read a character from the window. In no-delay mode, if
no input is waiting, the value ERR is returned. In delay mode, the program waits until the system passes
text through to the program. Depending on the setting of cbreak, this is after one character (cbreak
mode), or after the first newline (nocbreak mode). In half-delay mode, the program waits until a charac-
ter is typed or the specified timeout has been reached.

Unless noecho has been set, then the character will also be echoed into the designated window according
to the following rules: If the character is the current erase character, left arrow, or backspace, the
cursor is moved one space to the left and that screen position is erased as if delch had been called. If
the character value is any other KEY_ define, the user is alerted with a beep call. Otherwise the char-
acter is simply output to the screen.

If the window is not a pad, and it has been moved or modified since the last call to wrefresh, wrefresh
will be called before another character is read.

If keypad is TRUE, and a function key is pressed, the token for that function key is returned instead of
the raw characters. Possible function keys are defined in <curses.h> as macros with values outside the
range of 8-bit characters whose names begin with KEY_. Thus, a variable intended to hold the return value
of a function key must be of short size or larger.

When a character that could be the beginning of a function key is received (which, on modern terminals,
means an escape character), curses sets a timer. If the remainder of the sequence does not come in with-
in the designated time, the character is passed through; otherwise, the function key value is returned.
For this reason, many terminals experience a delay between the time a user presses the escape key and the
escape is returned to the program.

The ungetch routine places ch back onto the input queue to be returned by the next call to wgetch. There
is just one input queue for all windows.

это ещё не всё...

#132938

Пробовал сменить и инклуд и читать маны.

text

       int getch(void);
       int wgetch(WINDOW *win);
       int mvgetch(int y, int x);
       int mvwgetch(WINDOW *win, int y, int x);
       int ungetch(int ch);
       int has_key(int ch);

Понимаю что вопрос идиотский, но ни как не могу найти верного ответа.
Ошибка та же:
Ошибка сегментирования.

#132939

Пробовал по разному:
1) scanw("%c", &ch);
2) ch = getch();
3) int ch
ch = scanw("%i", &ch); // Но это даже глупо, простите.
Проблема как я вижу в том что тип char в Си отводит для себя в памяти значения от 0 до 255.
Русские символы в кодировке UTF-8 выходят за эти пределы.
Тогда встает вопрос - какой тип данных надо использовать в данном случае?
Если я вас правильно понял, то библиотека ncurses должна отводить под этот тип
большие значения, но в данном случае этого не происходит.
Тогда встает вопрос - почему так происходит?

#132961
hardorsoft написал:

1. Пробовал по разному...

2. Проблема как я вижу в том что тип char в Си отводит для себя в памяти значения от 0 до 255.
Русские символы в кодировке UTF-8 выходят за эти пределы.

3. Тогда встает вопрос - какой тип данных надо использовать в данном случае?

4. Если я вас правильно понял, то библиотека ncurses должна отводить под этот тип
большие значения, но в данном случае этого не происходит.
Тогда встает вопрос - почему так происходит?

Прежде всего я должен извиниться, за то что сказал "Ждите!", а сам пропал. По сути получилось кидалово.

Вот за что я люблю студентов, которые вгрызаются в проблему, так это за то, что они еще и меня ставят в тупик.
Да, я не знаю, как решается эта задача. Но с Вашей подачи тоже "влетел в проблему", Теперь спать не могу. Хочу разобраться с кириллицей в среде Линукса.

1. Тоже пробовал по разному. И даже, с позволения сказать, плюнул на Вашу задачу. Написал цикл, в котором принимаю один широкий символ и тут же его вывожу на консоль. Перепробовал как мне кажется тоже все варианты -- ноль!

Вывод на консоль константы (символ кириллицы) -- без проблем. А вот принять с консоли и тут же вывести -- не работает. Фигня какая-то. Значит, я чего-то не понимаю. То же рою землю, ищу как правильно это делается.

2. Я переформулирую Ваше высказывание -- в UTF-8 символы имеют переменную ширину в байтах. Значит, <см. п.3>

3. нужно работать не с байтами и словами, а использовать чаровские (char, character) средства, уповая на то, что библиотеки с ними умеют работать.

4. Не совсем правильно. Библиотека ncurses содержит функции для работы с консолями всех видов. Символы переменной ширины -- это не ее сильная сторона. Я так подозреваю, что даже вообще не работает. Библиотека ncursesw создана для работы как раз с символами переменной ширины.


Проблему непонимания как работать с кириллицей нельзя оставлять открытой. Нужно добить ее, и выработать какие-то рекомендации для тех, кто идет с нами или за нами, чтобы народ не наступал на эти же грабли. В общем, руки не опускаем. Работаем вместе. Приглашаем к диалогу всех, кто что-нибудь знает.

А проблема действительно существует, не надо смеяться. Например, во многих программах я замечаю, что текстовые редакторы не умеют правильно удалять символы кириллицы. Это я видел в редакторах-средах разработчика (то ли в geany, то ли в Code::Blocks). Стоит удалить одну букву в слове -- все происходит какая-то рассинхронизация и чтобы исправить слово, нужно его набрать заново. Т.е. синхронизацию восстанавливают символы "пробел". Т.е. на сколько я понимаю, даже у самих разработчиков ПО для разработчиков (извините за получившийся каламбур) еще нет однозначного понимания как работать с UTF-8.

#132992
hardorsoft написал:
3) int ch
ch = scanw("%i", &ch); // Но это даже глупо, простите.

почему-же? символ в utf-8 имеет размер 1, 2, и иногда даже 5 байт! к счастью для русского достаточно 2х байт, тип short. но лучше юзать int.

hardorsoft написал:
Проблема как я вижу в том что тип char в Си отводит для себя в памяти значения от 0 до 255.

неправильно. тип char имеет значение -128...127.
Следует учесть, что -1 это ещё и EOF, потому вашей программе невозможно отличить конец файла от символов. используйте int.

hardorsoft написал:
Тогда встает вопрос - какой тип данных надо использовать в данном случае?

эх...

text
#include <curses.h>

int main() { initscr(); move(10,30); printw("Hello world !!!"); refresh(); char s[256]; scanw("%s", s); printw("\n%s\n", s); getch(); endwin(); return 0; }

#133865

Убивать никого не надо, а вот почему функция не работает надо выяснить.

text

#include <curses.h>

int main() { initscr(); move(10,30); printw("Hello world !!!"); refresh(); char s[256]; scanw("%s", s); printw("\n%s\n", s); getch(); endwin(); return 0; }

работает.

text

/*
  Программа определяет гласная или согласная буква была введена
  с клавиатуры.
*/
#include <locale.h>
#include <curses.h>
#include <stdio.h>

#define YES 1 #define NO 0

glasn(ch) char ch; { static char gl[] = "АаЕеИиОоУуЫыЭэЮюЯя\0"; int i;

while(gl[i] && (gl[i] != ch)) i++; if(gl[i]) return(1); else return(0); }

main() { char ch; char *log; int flag; log = setlocale(LC_CTYPE,"ru_RU.UTF-8"); initscr(); printw("*** Проверяем гласная или согласная буква введена с клавиатуры. ***\n"); printw("Введите букву.\n --> "); scanw("%c", &ch);

до этой строки работает.
Дальше..

text

flag = glasn(ch);
  if(flag == YES) 
    printw("%c - буква гласная.\n", ch);
  else 
    printw("%c - буква согласная.\n", ch);
    
  printw("Нажмите <Enter> для выхода из программы");
  getch();
  endwin();
  return 0;
}

Ошибка сегментирования.
Не работает.
Меня это тоже убивает.

#133877
hardorsoft написал:
scanw("%c", &ch);
hardorsoft написал:
Меня это тоже убивает.

[console]$ xxd -c1
a
0000003: 61 a
0000004: 0a .
Ф
0000005: d0 .
0000006: a4 .
0000007: 0a .
[/console]
если нажать на 'a', получится 61.
если на "Ф", получится d0 и a4. ДВА БАЙТА!!!!
а в переменную char влезает только ОДИН байт.
что непонятно?
&ch - адрес переменной, размером один байт, вы туда пытаетесь записать 2. Неудивительно, что ядро прерывает работу вашей проги.

#134159
da Vinci написал:
Старший бит определяет один или больше байт в символе.

чё? вы бредите? пруф?

#134168
text
/*
  Программа определяет гласная или согласная буква была введена
  с клавиатуры.
*/
#include <locale.h>
#include <curses.h>
#include <stdio.h>
#include <string.h>

#define YES 1 #define NO 0

glasn(ch) char *ch; { const char *gl[] = {"А","а","Е","е","И","и","О","о","У","у","Ы","ы","Э","э","Ю","ю","Я","я",NULL}; int i;

while(gl[i] && !(strcmp(gl[i], ch))) i++; if(gl[i]) return(1); else return(0); }

main() { char ch[6]; char *log; int flag; log = setlocale(LC_CTYPE,"ru_RU.UTF-8"); initscr(); printw("*** Проверяем гласная или согласная буква введена с клавиатуры. ***\n"); printw("Введите букву.\n --> "); scanw("%s", &ch); flag = glasn(ch); if(flag == YES) printw("%s - буква гласная.\n", ch); else printw("%s - буква согласная.\n", ch); printw("Нажмите <Enter> для выхода из программы"); getch(); endwin(); return 0; }

Так работает

drBatty написал:
почему-же? символ в utf-8 имеет размер 1, 2, и иногда даже 5 байт! к счастью для русского достаточно 2х байт, тип short. но лучше юзать int.

А вот это реально бред.

#134216
da Vinci написал:
Так работает

и в чём разница между вашим и моим кодом?
http://linuxforum.ru/viewtopic.php?pid=149147#p149147
и там и там - РЕШЕТО.
надо scanw("%1s") писать, что-бы одну букву вводило. ваш код будет сегфолтится на седьмой букве, а мой на 257й. Вот и вся разница в наших (говно)кодах. :P

da Vinci написал:
А вот это реально бред.

бездоказательно

#134305
drBatty написал:
и в чём разница между вашим и моим кодом?

Разница в том, что при корректном вводе мой код делает то, что от него хочет топикстартер, а ваш код - это всего лишь хеловолд на ncurses с поддержкой utf-8.

drBatty написал:
и там и там - РЕШЕТО.

Можете взламывать мой код, я в суд не буду подавать.

drBatty написал:
бездоказательно

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

#134309
da Vinci написал:
Разница в том, что при корректном вводе мой код делает то, что от него хочет топикстартер, а ваш код - это всего лишь хеловолд на ncurses с поддержкой utf-8.

точно. а я и не заметил. я честно говоря и код-то по диагонали читал. просто нашёл в нём ошибку. исправлять - это вообще не моя забота. не?

da Vinci написал:
Можете взламывать мой код, я в суд не буду подавать.

могу. но зачем? достаточно указать на уязвимость, это вы её должны устранять, код-то не мой. Впрочем, я её вам уже устранил, ещё до написания эксплоита:

drBatty написал:
надо scanw("%1s") писать

(кстати, может не 1, а 2. я точно не помню, в чём считает scanw())

da Vinci написал:
Прежде чем требовать от меня доказательств, хотелось бы увидеть доказательство своего заявления в виде кода, а то не ясен смысл и смахивает на простой троллинг.

тупой троллинг - у вас. Вместо того, что-бы с фактами опровергнуть мои слова, вы написали просто

da Vinci написал:
А вот это реально бред.

мне что, сразу бежать за пруфами? Нет уж, раз вы назвали что-то "бредом", извольте это опровергнуть.
впрочем, вот по вашей-же ссылке:

рукопедия написал:
Остальные символы Юникода изображаются последовательностями длиной от 2 до 6 байт (реально только до 4 байт, поскольку использование кодов больше 221 не планируется)
drBatty написал:
почему-же? символ в utf-8 имеет размер 1, 2, и иногда даже 5 байт! к счастью для русского достаточно 2х байт, тип short. но лучше юзать int.

ну и где у меня "бред"? может и 6 быть.
далее википедия пишет:

0x00000000 — 0x0000007F 0xxxxxxx ASCII, в том числе английский алфавит, простейшие знаки препинания и арабские цифры
0x00000080 — 0x000007FF 110xxxxx 10xxxxxx кириллица, расширенная латиница, арабский, армянский, греческий, еврейский и коптский алфавит; сирийское письмо, тана, нко; МФА; некоторые знаки препинания
0x00000800 — 0x0000FFFF 1110xxxx 10xxxxxx 10xxxxxx все другие современные формы письменности, в том числе грузинский алфавит, индийское, китайское, корейское и японское письмо; сложные знаки препинания; математические и другие специальные символы
0x00010000 — 0x001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx музыкальные символы, редкие китайские иероглифы, вымершие формы письменности

Также теоретически возможны, но не включены в стандарты:
Unicode UTF-8
0x00200000 — 0x03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0x04000000 — 0x7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

Замечание: Символы, закодированные в UTF-8, могут быть длиной до шести байт, однако стандарт Unicode не определяет символов выше 0x10ffff, поэтому символы Unicode могут иметь максимальный размер в 4 байта в UTF-8.

хотя IRL и не употребляются 6и битывые^Wшестибайтовые символы, но кодеры должны учитывать их существование, как и существование несимволов. Потому как злоумышленник об этом прекрасно осведомлён.

#134314

ru.wikipedia.org/wiki/Порядок_байтов

drBatty написал:
символ в utf-8 имеет размер 1, 2, и иногда даже 5 байт!

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

Дальнейшее общение бессмысленно, умные сами поймут.

Войдите или зарегистрируйтесь, чтобы ответить.