Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Для новичков > [FAQ] Каракули вместо русских букв |
Автор: papam 26.12.2007, 18:06 | ||||||||||||||||||||||||||||||||||||||||||||
Всем Доброго времени суток! В данной статье речь идет о преобразовании символьной информации между двумя кодировками OEM и ANSI в ОС Windows Метод первый : Локализация Локализация в первую очередь используется в потоках ввода/вывода стандарьной библиотеки. Программа открывает для чтения существующий файл и закрывает его. В противном случае ругается. Имя файла вводит пользователь. Причем предполагается, что файл находится в текущем каталоге (каталоге с нашим проектом). У меня проект называется Listing5, поэтому я в этой самой папке заранее создал текстовый файлик с именем Файлик.txt Начнем с функции setlocale. Она нужна для установки, изменения или определения программной локали и имеет такой вид:
category – категория (тип) изменения локали; locale – имя (название) локали; Первый параметр может принимать шесть различных значений, нам же подойдут два: LC_ALL или LC_CTYPE. При использовании первого варианта изменения коснутся всех категорий, при использовании второго варианта изменения коснутся функций, работающих с символами. Второй параметр представляет собой имя национальных особенностей. Это может быть просто имя, например, “French” или же имя вместе с указанным через точку номером кодовой страницы “French_Canada.1252” или же только номер кодовой страницы “.866” . Можно не вспоминать номер кодовой страницы и поставить “.OCP” – текущий номер для OEM, определенный в операционной системе. Дальше в программе объявляется prompt - строка С-стиля. Здесь же она инициализируется строковым литералом с префиксом L. Префикс L указывает, что данная строка является строкой символов из расширенного набора (Unicode) и имеет тип const wchar_t[]. Дальше объявляется строка типа wstring. Это тот же самый string, но состоящий не из ANSI-символов, а из Unicode-символов (тип которых wchar_t). Затем идет диалог с пользователем и инициализация filename строчкой, которую вводит пользователь. Последняя часть – собственно действия над файлом. Объявляется указатель на структуру FILE, которая содержит в себе информацию о текущем состоянии потока; используется во всех потоковых операциях ввода/вывода. Затем файл открывается с помощью функции [B]_wfopen[/b] (версия функции fopen стандартной библиотеки, но для Unicode). Эта функция принимает в качестве параметров имя файла и атрибут. В нашем случае это
В случае ошибки функция
Функция
Пример:
Пример 2:
Метод второй: функции SetConsoleCP и SetConsoleOutputCP Вот эти функции:
Разница между этими двумя функциями такая:
Чтобы узнать текущий номер кодовой страницы консоли, мы можем использовать вот эти две функции:
Эти функции не принимают параметров, а возвращают текущий номер кодовой страницы консоли. Разница между ними такая же, как и у двух предыдущих - все ясно из названия. Более то, чтобы не мучаться с таблицами всех этих номеров, есть ещё две функции:
Обе функции без параметров, первая возвращает текущий номер кодовой страницы в ANSI, вторая - в OEM. Следовательно, эти функции очень удобно использовать в качестве параметров для функций:
Эти функции работают не всегда. Смотрите, какие у них требования: Client: Included in Windows XP, Windows 2000 Professional, and Windows NT Workstation. Server: Included in Windows Server 2003, Windows 2000 Server, and Windows NT Server. Пример:
Метод третий: функции CharToOem и OemToChar Две API-шные функции:
В качестве первого параметра функции принимают С-строчку, которую преобразуем, в качестве второго - строчку, в которую будет помещен результат преобразования. Если эти функции используются как ANSI-функции, то в качестве обоих параметров можно передавать одну и ту же строку. В таком случае переданная функции строчка просто преобразуется в нужную нам кодировку (без какого-либо буфера). Есть еще пара вариантов:
Работают также как и CharToOem/OemToChar, с той лишь разницей, что в качестве третьего параметра принимают количество символов в строке, которые надо преобразовать.
Эти функции преобразуют строчку из ANSI в OEM и наоборот. Это значит, что нам вообще не надо мучаться с номерами кодовых страниц и т.п. Но проблема в том, что преобразуют они лишь одну строчку. А сколько нам в программе может понадобиться выводить и вводить из консоли строчки - неизвестно, но каждую строчку (русскую) надо преобразовывать, следовательно, каждый раз при этом надо будет вызывать эти функции. Получается код, довольно захламленный постоянными вызовами одних и тех же функций. Но что поделаешь… Чтобы избежать неудобств при использовании таких функций, можете, скажем, написать свой собственный класс, который будет чем-то вроде оболочки для выполнения этих операций. Например, класс, в котором будут перегружены операторы ввода (operator >>) и вывода (operator <<) таким образом, чтобы перед самим вводом/выводом выполнять необходимое преобразование. Чтобы для практики такой класс имел значение и был полезен, надо постараться - учесть всевозможные переполнения буфера, преобразования типов и т.д. и т.п. В общем, безопасность и функциональность - очень важны. А если вдруг захочется попробовать создать класс, производный от iostream, учтите, что в нём виртуальных функций нет. На мой взгляд, для теоретических знаний языка С++ это может быть полезно, но для практики проще (а пожалуй и лучше) просто явно вызывать функцию
Пример:
Метод четвертый: CString::AnsiToOem и CString::OemToANSI В MFC для работы со строками есть специальный класс CString. И у этого замечательного класса есть кроме других два метода:
Эти функции осуществляют необходимое преобразование над объектом класса CString. Класс CString хорош, с ним очень удобно работать и уж лучше всегда по мере возможности использовать его вместо строк стиля С (массива символов). Хотя можно, конечно, и шаблон string из STL (Standard template library - Стандартная Библиотека Шаблонов, она входит в стандартную библиотеку). Пример:
|
Автор: nickless 26.12.2007, 18:38 | ||
Предлагаю написать в начале статьи, что речь идёт исключительно о windows, а то в линуксе, благодаря utf-8, прекрасно работают программки вроде
![]() |
Автор: papam 26.12.2007, 18:46 |
Можно прикрепить эти примеры к статье, тут 3 скриншота к каждому методу,я забыл, прошу прощения |
Автор: archimed7592 26.12.2007, 20:40 |
papam, всё пока не читал - только пробежался. Первые претензии: 1. Убрать из текста слова "отродья", "ни хрена", "мессага", "фига" и пр. Воспользоваться более культурными синонимами. Короче говоря, переработать текст, чтобы его можно было "отдавать в печать". 1.1. Исправить множественные опечатки(в т.ч., часто встречающийся "<" вместо "<"). 2. Оформить все строки кода(в т.ч. прототипы ф-ций) в тэги code. 3. Я примеры не смотрел, но, если они не большие, то добавить их в текст, причём не просто в конце, а к каждому методу отдельно. All, просьба оценить техническую корректность текста, причём, подойти к этому нужно настолько критично, насколько это возможно ![]() |
Автор: archimed7592 26.12.2007, 22:59 |
papam, как я вижу, ты немного подредактировал текст. Смотри, если человек приходит с вопросом "почему у меня каракули в программе", то, очевидно, что он умеет создавать проект в своей любимой IDE(которая не обязательно будет MSVC), так что, думаю, что нужно убрать подробности о том, как создавать проект, какие строки и откуда нужно удалять и т.п. Схема должна быть такой: описание метода, потом слова "Пример:" и сам пример, обрамлённый тегом code. Причём, обязательно откорректировать примеры, убрав оттуда MS-специфичные вещи, аля _t, TCHAR и пр.(если метод не ориентирован исключительно на MSVC). main не должна принимать аргументы, если они ей не нужны. |
Автор: papam 26.12.2007, 23:42 |
archimed7592, историю убрать? |
Автор: archimed7592 26.12.2007, 23:45 |
papam, я пока подробно не читал. Не знаю. После НГ будем более подробно разбираться. Сейчас, к сожалению, времени совсем нет. |
Автор: zkv 27.12.2007, 17:29 | ||
да, тоже самое, давайте до после НГ отложим. ![]() |
Автор: zkv 27.12.2007, 23:55 | ||||
да про это стоит упомянуть. для сведения, посмотри http://forum.vingrad.ru/index.php?showtopic=186208&view=findpost&p=1350138 (помню кто-то использовал преобразование к unsigned в качестве решения вопроса) решение с локалью думаю стоит вынести в первую очередь, и упомянуть хотя-бы о существовании std::locale а такие вещи:
окучить бы не мешало.. разве локали можно использовать только вместе с юникодом? |
Автор: Winprogrammer 29.12.2007, 00:38 | ||||
papam,
опиши все параметры. Они могут пригодиться.
Сперва опиши в чем заключается второй метод. А потом уже приступай к описанию функций, как ты это сделал в первом методе. Третий и четвертый методы имеют тот же недостаток оформления - нет описания. Это функции какого API ???? API - это Application Programming Interface. Как таковых API много. Для опытного человека понятно, что это WinAPI. А для новичка - это тёмные дебри. Используй более оффициальный язык. ЗЫ: В принципе статья - отличная! только ее надо "вылизать"! ;) Вот пока все, что могу сказать. Но над статьей надо еще работать... работать... и работать... ![]() |
Автор: archimed7592 29.12.2007, 17:35 | ||||||||||||||
Что ж. Начну, наверное, с этой статьи. А точнее с первого метода(локаль). Сразу предупрежу, что, даже если покажется, что я как-то грубо высказываюсь - это не означает, что я к Вам плохо отношусь. Можете считать, что я выражаю мысли новичка, который читает Вашу статью ![]()
OEM - это что за кодировка такая? Неплохо было бы упомянуть. ANSI - это что за кодировка? А Unicode мы в этой статье не рассматриваем?
Какая ещё программа? Какой файл? Зачем ругаться? О чём вообще речь?
Отлично, а в каком заголовочном файле эта ф-ция объявлена?
Ммм. Не обязательно каждое слово оформлять в тэг code. Можно было бы либо просто не оформлять никак, либо выделить моноширинным шрифтом(предпочтительнее). К примеру так: category – категория (тип) изменения локали. [font=courier]category[/font] – категория (тип) изменения локали.
Очень интересно, хорошо бы ещё понять к чему мне нужно было читать этот абзац. А ещё лучше - понять бы к чему нужен был предыдущий абзац между сигнатурой ф-ции и описанием её параметров. В какой программе? Вы о чём? Кем кем? Литералом? А это кто?
Хнык-хнык. А мой компилятор, говорит, что не знает о такой ф-ции :'(.
А зачем нам юникод сдался? Мне что, из-за этих каракуль всю программу под юникод переписывать? Я что-то не понял - пример для Си или для С++? Хто есть my_codecvt? Мой компилятор отказывается это компилировать. Хнык-хнык. Теперь в общем и целом: повествование оформленно как описание примера. Пример почему-то идёт после самого описания. Зачем-то смешаны и Си и С++. Пожелания(кроме процитированных багов): 1. перед самими методами в двух словах написать откуда вообще взялась проблема. 2. перед методами перечислить список методов с очень кратким описанием. 3. метод: сначала описание метода как такого, потом пример, потом описание примера. 4. в примерах не должно быть ничего лишнего(зачем там вообще понадобились файлы? а юникод?) |
Автор: papam 29.12.2007, 18:05 |
archimed7592,завтра постараюсь исправить все. |
Автор: archimed7592 17.1.2008, 19:40 |
papam, как дела со статьей продвигаются? ![]() |
Автор: archimed7592 20.1.2008, 19:46 | ||
|
Автор: JackYF 20.1.2008, 20:06 |
Если 1) все примеры, наличествующие сейчас в статье, работоспособны 2) никто из камрадов не возьмётся , то могу я с ограничениями - винды у меня не водится, поэтому могу подправить только стиль и описание до удобоваримого вида. З.Ы. ![]() |
Автор: archimed7592 20.1.2008, 20:11 | ||
Ок, есть желающие проверить? ![]() |
Автор: Kanes 6.2.2008, 10:43 | ||||||||
Пока проверил 2:
Работает без проблем, единственное надо дополнить огранечения по применению, т.е. установка шрифта Lucida Console и не возможность работы в полноэкранном режиме
Работает, но, может я конечно что-то сам напутал, короче, для простоты написал ф-цию ToRus:
и простенький пример:
Так вот, компилятор собирает все нормально, только при работе программа вылетает когда доходит до второго cout'а P.S. Скоро проверю остольные способы |
Автор: bsa 6.2.2008, 13:36 | ||
Kanes 1. sizeof(str) в твоем случае всегда равно 4 (размер указателя, а не массива) 2. ты сделал new[], а кто будет делать delete[]?
|
Автор: Damarus 6.2.2008, 17:40 | ||||||||
MinGW 3.4.5 |
Автор: JackYF 6.2.2008, 18:48 |
дык и верно, что за .OCP? почему не .1251, не CP1251? |
Автор: bsa 6.2.2008, 18:52 |
Damarus, посмотри внимательно статью. там перед текстовыми константами буква L стоит. |
Автор: Damarus 6.2.2008, 20:50 | ||
1. Почему .OCP надо спрашивать у papam'а. 2. С .1251, CP1251, ru_RU, Russian_Russia.1251, French_Canada.1252 и т.д. результат тот-же.
libstdc++ из mingw не поддерживает unicode. Я думаю это вообще не должно играть роли. PS. Вообще я хотел только показать, что не на всех компиляторах пример будет работать. |
Автор: JackYF 6.2.2008, 21:13 | ||
Нет, ну это да. Поэтому столько костылей и приводится, чтобы хоть что-нибудь работало в виндовой консоли... З.Ы. Недавно разбирался с QString, могу показать, как сделать ещё один костыль на основе Qt4Core ![]() |
Автор: Kanes 6.2.2008, 21:51 |
ИМХО, пока реально работает только SetConsoleCP и SetConsoleOutputCP, проверял на * Microsoft Visual C++ Free Toolkit 2003 * Borland C++ Compiler 5.5 * mingw |
Автор: LinuxanT 7.2.2008, 01:18 | ||
у мея так прокатило... в консоли
|
Автор: bsa 7.2.2008, 01:44 |
LinuxanT, какой компилятор? |
Автор: Djinn 7.2.2008, 12:12 | ||
Сработало в MSVC 2005 Пасиба большое ! ![]() ![]() |
Автор: Djinn 7.2.2008, 19:46 | ||
правда у мея так прокатило... в консоли
|
Автор: JackYF 7.2.2008, 19:48 |
Djinn, ? |
Автор: Djinn 7.2.2008, 21:04 |
у меня без него работает ! |
Автор: Kanes 7.2.2008, 21:34 |
А у меня и с ним не работает(((( |
Автор: LinuxanT 8.2.2008, 04:55 | ||
MSVC 2008 pro на визуалках работает..... |
Автор: Djinn 8.2.2008, 16:30 |
Не, у меня у знакомого такой как у мя компилятор и говорит не работает :( интересно от чего это зависит ? :( |
Автор: Kanes 8.2.2008, 17:53 |
У меня на mingw заработало |
Автор: Djinn 17.2.2008, 10:53 |
Вобщем работает оно у тех кто делает проект в Unicode, поэтому оно работает у всех =) просто стоит настроить немного.... 1 параметр ![]() |
Автор: aleknek 1.3.2008, 18:47 | ||
Написал простенькую программу, которая запрашивает имя пользователя и фамилию, а затем выводит отдельной строкой. Так вот подключил локаль, как обычно делаю, для отображения русского текста, при наборе все нормально, а вот при выводе получается абракадабра всякая, уже заходил в настройки проекта менял Use Unicode Character Set на Use Multi-Byte Character Set, толку никакого, подскажите, пожалуйста, как быть ??? Все это пишется в среде Microsoft Visual Studio 2005 :
|
Автор: rugo 26.3.2008, 15:00 | ||
Подскажите,почему у меня не работает конвертация кодировки Dos в Win при вызове следующей функции
|
Автор: rugo 26.3.2008, 17:04 | ||||
Разобрался сам. |
Автор: Baton2007 12.5.2008, 20:04 | ||||
Извеняюсь если такое уже было я лично пользуюсь такой функцией:
Вот пример:
|
Автор: mmvds 10.8.2008, 01:08 | ||
Открываем свойства запущенного окна, шрифт - Lucida Console, для всех окон и ни каких проблем |
Автор: миг 16.1.2011, 10:32 | ||
У меня с .OCP и с .866 все работает.. а с.1251 в консоли кракозябры ![]() |
Автор: wester 16.1.2011, 19:39 |
миг, посмотри на даты сообщений. и отложи лопату в сторону |
Автор: миг 16.1.2011, 20:01 |
wester, Я видел) Просто консольные приложение под виндовс используют кодировку Доса.. а ребята пытаются установить кодировку для виндовс и вывести символы.. Не смог не пройти мимо такого) |
Автор: xvr 17.1.2011, 14:43 | ||||||
'Консольным программам' кодировка по барабану - их просят вывести байтики, они их и выводят. Дальше личное дело самой консоли. В девичестве она печатала байтики в cp866
![]() |
Автор: миг 17.1.2011, 20:26 | ||
А в каких по вашему операционках используют консоль с кодовой страницей cp1251? А ничего если по умолчанию кодовые страницы буферов консоли устанавливаются на кодовую страницу производителя OEM? И что по вашему должно произойти если по умолчанию стоит кодовая страница cp866.. а пытаются вывести символы в кодировке cp1251? |
Автор: xvr 17.1.2011, 21:44 | ||||
|
Автор: миг 17.1.2011, 22:34 |
Да. но это уже было не в том примере о котором я говорил . |
Автор: xvr 18.1.2011, 10:59 | ||||
С большим трудом нашел тот пример. Там локаль для консоли пытались выставить программно. Что так же нормально работает. 'Ребята' всего лишь напутали с именем локали, а так там все правильно.
![]() |
Автор: Rutti 30.5.2012, 13:04 | ||
В поисках настройки кириллицы наткнулся на такой вот исходник:
который прекрасно отображает кириллицу без смены шрифта в окне консоли в Windows. Заметьте - комментарий и текст написаны в разных кодировках. Как это делалось? Писатель постоянно переключал кодовые страницы? Или как? В Dev C++ , кстати, не нашёл как можно поменять проект на Unicode - возможно ли вообще? |
Автор: bsa 30.5.2012, 17:49 |
Rutti, пишешь код каким-нибудь досовым редактором (например, из borland c++ 3.1), сохраняешь. загружаешь в виндовый редактор и пишешь комментарии. но это для понимающих толк в извращенствах. |
Автор: Rutti 30.5.2012, 20:13 |
Вот разве что. |
Автор: Rutti 30.5.2012, 22:55 |
У меня кракозябры А как этот проект создать в Dev C++ ? |
Автор: bsa 31.5.2012, 23:22 |
Rutti, http://forum.vingrad.ru/index.php?showtopic=269794&view=findpost&p=2086699 |
Автор: Rutti 1.6.2012, 19:59 | ||
А почему тогда в GCC не работает? Не универсальное решение? |
Автор: GraNit 2.6.2012, 06:01 |
Господа, значит остается редактировать вывод в какой-нибудь Far-е? Для windows работает только system("chcp 1251>nul"); |