<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF100-101:Урок грамматики - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF100-101:%D0%A3%D1%80%D0%BE%D0%BA_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B8&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Wed, 13 May 2026 21:32:20 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel: /* Очепятки не пройдут */</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF100-101:%D0%A3%D1%80%D0%BE%D0%BA_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B8&amp;diff=7532&amp;oldid=prev</link>
			<description>&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Очепятки не пройдут&lt;/span&gt;&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;← Предыдущая&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Версия 11:00, 6 апреля 2009&lt;/td&gt;
			&lt;/tr&gt;
		&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 18:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 18:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;примеров будет дан с привязкой к ''GTK+'' (мы публиковали серию статей&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;примеров будет дан с привязкой к ''GTK+'' (мы публиковали серию статей&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;о нем в период с [[LXF86:GTK+|LXF86 по LXF95]], так что вы найдете все эти уроки&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;о нем в период с [[LXF86:GTK+|LXF86 по LXF95]], так что вы найдете все эти уроки&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;на LXFDVD&lt;/del&gt;), однако, изложенные общие принципы работы применимы&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;по ссылке&lt;/ins&gt;), однако, изложенные общие принципы работы применимы&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;к любой библиотеке. В конце материала будет уделено немного места&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;к любой библиотеке. В конце материала будет уделено немного места&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;еще одному движку – ''Enchant'', для сравнения. Также обратите внимание на ''Hunspell'' – он уже используется в ''OpenOffice.org'', а со временем&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;еще одному движку – ''Enchant'', для сравнения. Также обратите внимание на ''Hunspell'' – он уже используется в ''OpenOffice.org'', а со временем&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Mon, 06 Apr 2009 11:00:43 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF100-101:%D0%A3%D1%80%D0%BE%D0%BA_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B8</comments>		</item>
		<item>
			<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF100-101:%D0%A3%D1%80%D0%BE%D0%BA_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B8&amp;diff=7531&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: ''Aspell'' и ''Enchant'' Снабдите свои программы функцией проверки орфографии&lt;br /&gt;
&lt;br /&gt;
==Очепятки не пройдут==&lt;br /&gt;
&lt;br /&gt;
: Пытаясь набрать это предложение, наш редактор допустил три описки. И что бы он делал без модуля проверки орфографии? '''Петр Семилетов''' расскажет, как прикрутить такой к вашей программе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF100_90_1.jpg|300px]]|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Потребность в движках проверки орфографии растет с развитием общения через Интернет. Если раньше ошибки правописания подсвечивались разве что в текстовых редакторах и процессорах, то нынче это делают и браузеры, и программы для&lt;br /&gt;
обмена мгновенными сообщениями. Сложно сказать, улучшает ли&lt;br /&gt;
компьютерная проверка орфографии грамотность человека. С одной&lt;br /&gt;
стороны, пользователь сразу видит свои ошибки и в следующий раз&lt;br /&gt;
может написать слово правильно. Однако, полагаясь в грамотности на&lt;br /&gt;
машину, не потакает ли человек лени своего разума?&lt;br /&gt;
&lt;br /&gt;
На этом уроке мы не будем задаваться столь философскими вопросами, а просто рассмотрим, как задействовать движок проверки правописания ''Aspell'' в своих программах. Мы будем использовать API для языка ''С'', а для большей наглядности практического применения ряд&lt;br /&gt;
примеров будет дан с привязкой к ''GTK+'' (мы публиковали серию статей&lt;br /&gt;
о нем в период с [[LXF86:GTK+|LXF86 по LXF95]], так что вы найдете все эти уроки&lt;br /&gt;
на LXFDVD), однако, изложенные общие принципы работы применимы&lt;br /&gt;
к любой библиотеке. В конце материала будет уделено немного места&lt;br /&gt;
еще одному движку – ''Enchant'', для сравнения. Также обратите внимание на ''Hunspell'' – он уже используется в ''OpenOffice.org'', а со временем&lt;br /&gt;
должен заменить ''MySpell'' и в продуктах Mozilla.&lt;br /&gt;
&lt;br /&gt;
===ЧАСТЬ 1: GNU ''Aspell''===&lt;br /&gt;
&lt;br /&gt;
''Aspell'' (http://aspell.net) – один из самых популярных движков проверки&lt;br /&gt;
орфографии. Он состоит из консольной программы (которой, помимо&lt;br /&gt;
прочего, можно передавать данные через канал), а также библиотеки с&lt;br /&gt;
«сишным» API, хотя сам ''Aspell'' написан на ''С++''. Работы с каналами мы&lt;br /&gt;
касаться не будем.&lt;br /&gt;
&lt;br /&gt;
Описанию API в документации ''Aspell'' уделено мало внимания –&lt;br /&gt;
несколько примеров да примечания. Чтобы использовать ''Aspell'' в своей&lt;br /&gt;
программе, надо изучить его исходные тексты – причем не только заголовочные файлы. Но сначала не будет излишним прочесть эту статью.&lt;br /&gt;
&lt;br /&gt;
Начнем с проверки, установлена ли ''LibAspell'' в системе пользователя, и включения ее в параметры компилятора. По какой-то причине библиотека не оснащена модулем для ''pkg-config'', поэтому искать '''pc'''-файл&lt;br /&gt;
бесполезно. Вместо этого, в случае ''Autotools'', следует добавить в файл&lt;br /&gt;
'''configure.in''' (который будет обработан '''autogen.sh''' для создания скрипта&lt;br /&gt;
''configure'') макросы для проверки наличия заголовочного файла '''aspell.h''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c_mac&amp;gt;&lt;br /&gt;
 AC_CHECK_HEADER(aspell.h,&lt;br /&gt;
        LIBS=”$LIBS -laspell”&lt;br /&gt;
        AC_DEFINE(HAVE_LIBASPELL, 1, [use aspell]),&lt;br /&gt;
            [AC_MSG_ERROR([cannot find header for libaspell])]&lt;br /&gt;
        )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомню формат макроса '''AC_CHECK_HEADERS''':&lt;br /&gt;
&lt;br /&gt;
 AC_CHECK_HEADERS (имя заголовочного файла, [действие в положительном случае], [действие в отрицательном])&lt;br /&gt;
&lt;br /&gt;
Итак, мы проверяем, есть ли в системе файл '''aspell.h'''. Если есть, то&lt;br /&gt;
в файле '''config.h''' определяется флаг '''HAVE_LIBASPELL''', чтобы потом в&lt;br /&gt;
коде программы мы могли написать что-то вроде:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 #ifdef HAVE_LIBASPELL&lt;br /&gt;
 #include “aspell.h”&lt;br /&gt;
 #endif&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В приведенном выше примере, для вывода сообщения об ошибке был использован макрос '''AC_MSG_ERROR'''. Помимо прочего, он вызывает прекращение работы сценария '''configure'''. То есть, если '''aspell.h''' не была найден, то и настройка исходных текстов будет провалена. Если&lt;br /&gt;
такое поведение нежелательно, а наличие '''aspell.h''' – необязательное&lt;br /&gt;
условие для сборки вашей программы, то проверка может выглядеть&lt;br /&gt;
немного иначе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c_mac&amp;gt;&lt;br /&gt;
 AC_CHECK_HEADER(aspell.h,&lt;br /&gt;
            LIBS=”$LIBS -laspell”&lt;br /&gt;
            AC_DEFINE(HAVE_LIBASPELL, 1, [use aspell]),&lt;br /&gt;
                         echo “aspell.h not found”&lt;br /&gt;
            )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь, вместо использования сурового макроса мы просто выводим сообщение, что '''aspell.h''' не найден.&lt;br /&gt;
&lt;br /&gt;
Приступим к работе с библиотекой. Первым делом создадим экземпляр класса '''AspellConfig'''. Класс этот служит для управления всякими&lt;br /&gt;
настройками. Именно всякими, поскольку он не входит в инкапсуляцию&lt;br /&gt;
других классов ''Aspell'', но может быть применен для изменения настроек других классов. Создадим экземпляр:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 AspellConfig *config = new_aspell_config ();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После окончания работы его надо будет удалить при помощи функции '''delete_aspell_config()'''. Значения полей в классе можно менять при&lt;br /&gt;
помощи функции '''aspell_config_replace()'''. Формат вызова таков:&lt;br /&gt;
&lt;br /&gt;
 aspell_config_replace (config, имя переменной-поля, новое значение);&lt;br /&gt;
&lt;br /&gt;
Давайте для примера настроим класс под русскую локаль и кодировку UTF-8:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 aspell_config_replace (config, “lang”, “ru”);&lt;br /&gt;
 aspell_config_replace (config, “encoding”, “UTF-8”);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Замечу, что у пользователя может быть другая локаль, тогда ваше ‘'''ru'''’ будет бесполезным. Поэтому лучше определить локаль программно. Для этого нужно прочитать значение переменной окружения&lt;br /&gt;
‘'''LANG''';. В ''GTK+'' это делается вот так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 gchar *lang = g_getenv (“LANG”);&lt;br /&gt;
 if (lang)&lt;br /&gt;
    aspell_config_replace (config, “lang”, lang);&lt;br /&gt;
 else&lt;br /&gt;
    aspell_config_replace (config, “lang”, “C”);&lt;br /&gt;
 aspell_config_replace (config, “encoding”, “UTF-8”);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если, вдруг, переменная ‘'''LANG'''’ не установлена, то мы используем&lt;br /&gt;
значение ‘'''C'''’ (стандартная англоязычная локаль). Что до кодировки, то&lt;br /&gt;
указывая UTF-8, мы сообщаем движку ''Aspell'', какую кодировку будем&lt;br /&gt;
использовать для обмена данными с ним. Изнутри ''Aspell'' восьмибитный, но мы можем передавать ему текст в UTF-8 и получать его обратно тоже в UTF-8. Полученным текстом может быть, например, список предположительно правильных написаний для данного (переданного&lt;br /&gt;
в параметре функции проверки) слова. Конечно, никто не мешает вам&lt;br /&gt;
использовать другую кодировку, но в современных условиях UTF-8 –&lt;br /&gt;
лучшая, если ваша библиотека виджетов ее поддерживает.&lt;br /&gt;
&lt;br /&gt;
Еще одна подробность – в указанной вами кодировке ''Aspell'' будет&lt;br /&gt;
сохранять новые слова в пользовательском словаре. ''Aspell'' создает&lt;br /&gt;
такие словари в отдельных файлах (по одному на каждый использованный языковый модуль – то есть на тот модуль, куда было виртуально добавлено слово). Файлы эти лежат в домашнем каталоге пользователя. Например, имя файла с пользовательским словарем для русского&lt;br /&gt;
языка – '''~/.aspell.ru.pws'''.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что после каждой смены в движке текущего языка надо снова задавать кодировку, иначе движок будет работать в&lt;br /&gt;
кодировке по умолчанию. Если явно не указать кодировку для русского, ею будет KOI8-R.&lt;br /&gt;
&lt;br /&gt;
Чтобы обеспечить программу возможностью использовать любой&lt;br /&gt;
доступный модуль проверки орфографии, необходимо получить список установленных модулей. В приведенном ниже примере мы получаем такой список и выводим имена модулей в консоль:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 const AspellDictInfo *entry;&lt;br /&gt;
 AspellConfig *config = new_aspell_config ();&lt;br /&gt;
 AspellDictInfoList *dlist = get_aspell_dict_info_list (config);&lt;br /&gt;
 AspellDictInfoEnumeration *dels = aspell_dict_info_list_elements (dlist);&lt;br /&gt;
 while ((entry = aspell_dict_info_enumeration_next (dels)) != 0)&lt;br /&gt;
      if (entry)&lt;br /&gt;
         printf (“%s\n”, entry-&amp;gt;name);&lt;br /&gt;
 delete_aspell_dict_info_enumeration (dels);&lt;br /&gt;
 delete_aspell_config (config);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итогом работы этого кода будет нечто вроде:&lt;br /&gt;
&lt;br /&gt;
 ru&lt;br /&gt;
 en&lt;br /&gt;
 en_CA&lt;br /&gt;
&lt;br /&gt;
и так далее.&lt;br /&gt;
&lt;br /&gt;
Полученные названия можно использовать в '''aspell_config_replace()''', чтобы установить локаль движка ''Aspell'':&lt;br /&gt;
&lt;br /&gt;
 aspell_config_replace (config, “lang”, локаль);&lt;br /&gt;
&lt;br /&gt;
Теперь у нас есть все знания для того, чтобы настроить язык и&lt;br /&gt;
кодировку. Но этого мало. Надо создать на основе этих данных экземпляр класса, отвечающего за проверку орфографии. Для этого API предоставляет нам две функции, которые мы рассмотрим подробно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 struct AspellCanHaveError* new_aspell_speller (struct AspellConfig *config);&lt;br /&gt;
 struct AspellSpeller* to_aspell_speller (struct AspellCanHaveError *obj);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Внутри первой функции происходят любопытные вещи. ''Aspell'',&lt;br /&gt;
как уже упоминалось выше, написан на ''С++'', и «сишной» структуре&lt;br /&gt;
'''AspellCanHaveError''' соответствует внутренний класс '''CanHaveError''', а '''AspellSpeller''' – класс '''Speller'''. Итак, функция '''new_aspell_speller()''' создает экземпляр класса '''Speller'''. В случае ошибки '''new_aspell_speller()'''&lt;br /&gt;
возвращает экземпляр '''CanHaveError'''. В противном же случае возвращается '''Speller''', которого нужно «вытащить» из '''CanHaveError''' функцией '''to_aspell_speller()'''. Я понимаю, что API можно было сделать более&lt;br /&gt;
логичным.&lt;br /&gt;
&lt;br /&gt;
Вот пример. Сначала вызываем '''new_aspell_speller''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 AspellCanHaveError *possible_err = new_aspell_speller(spell_config);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На этом этапе мы не знаем, что вернулось в '''possible_err''' на самом деле – экземпляр '''AspellCanHaveError''' или '''AspellSpeller'''. Поэтому объявляем '''spell_checker''' и проверяем '''possible_err''' – то есть вернулась ли&lt;br /&gt;
ошибка. Если да, то печатаем сообщение об ошибке, а если нет, то&lt;br /&gt;
функцией '''to_aspell_speller''' приводим '''possible_err''' к типу '''AspellSpeller'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 AspellSpeller *spell_checker;&lt;br /&gt;
 if (aspell_error_number (possible_err) != 0)&lt;br /&gt;
     printf (“%s\n”, aspell_error_message (possible_err));&lt;br /&gt;
 else&lt;br /&gt;
    spell_checker = to_aspell_speller (possible_err);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пара слов об освобождении памяти. Память для '''AspellCanHaveError'''&lt;br /&gt;
освобождается с помощью функции '''delete_aspell_can_have_error()''',&lt;br /&gt;
а для '''AspellSpeller – delete_aspell_speller()'''. В приведенном выше&lt;br /&gt;
коде, будь он в рабочей программе, освобождать память следовало бы так. Если вернулся экземпляр '''AspellSpeller''', то память для&lt;br /&gt;
'''AspellCanHaveError''' уже не нужно освобождать, потому что на самом-то&lt;br /&gt;
деле в '''possible_err''' хранится не экземпляр '''AspellCanHaveError''', а экземпляр '''AspellSpeller''', и вызовы обеих функций уничтожения объекта приведут к ошибке сегментации во второй из них.&lt;br /&gt;
&lt;br /&gt;
Лучший вариант работы с памятью таков. Если вернулся&lt;br /&gt;
'''AspellCanHaveError''' – выполняем для него '''delete_aspell_can_have_error()'''&lt;br /&gt;
и завершаем код проверки орфографии. Если вернулся '''AspellSpeller''',&lt;br /&gt;
то проверяем орфографию и очищаем память с помощью '''delete_aspell_speller()''', однако '''delete_aspell_can_have_error()''' для '''possible_err'''&lt;br /&gt;
уже НЕ вызываем.&lt;br /&gt;
&lt;br /&gt;
====Тонкая красная линия====&lt;br /&gt;
&lt;br /&gt;
Получив на руки работоспособный экземпляр '''AspellSpeller''', мы, наконец, можем проверить написание слова. Для этого служит функция&lt;br /&gt;
'''aspell_speller_check()''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 int aspell_speller_check (struct AspellSpeller *speller, const char *word, int word_size);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Она возвращает нулевое значение, если слова нет в словаре, '''1''' – если есть, и '''-1''' в случае возникновения ошибки (в движке). То есть в&lt;br /&gt;
рабочем коде, если нам нужно просто проверить, есть слово в словаре или нет, и нас не волнует возможная внутренняя ошибка, то можно&lt;br /&gt;
писать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 if (! aspell_speller_check (параметры)) {&lt;br /&gt;
    делаем что-то - например, подчеркиваем ошибочное слово&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Рассмотрим параметры функции поподробнее. Со speller’ом все&lt;br /&gt;
понятно. Слово '''word''', передаваемое для проверки, должно быть в той&lt;br /&gt;
кодировке, которую вы задали для словарного модуля. '''word_size''' – размер (в байтах) этого слова, может быть '''-1''', если мы имеем дело со&lt;br /&gt;
строкой, завершающейся нулем (‘'''\0'''’). Обычно так оно и есть.&lt;br /&gt;
&lt;br /&gt;
Попробуем ''Aspell'' в деле. Как проверить орфографию в стандартном виджете текстового редактора ''GTK+'' (да и в ''GtkSourceView'', [[LXF97:GtkSourceView|см. LXF97]])?&lt;br /&gt;
&lt;br /&gt;
Как вы, наверное, знаете, изменение атрибутов участков текста в '''GtkTextView''' осуществляется с помощью объектов-тэгов. Тэг несет в себе&lt;br /&gt;
параметры шрифта, цвет и так далее. Тэги хранятся в таблице, которая&lt;br /&gt;
подключается к '''GtkTextBuffer''' (именно к буферу, а не к '''GtkTextView''').&lt;br /&gt;
Сначала надо создать таблицу, а затем поместить в нее тэг.&lt;br /&gt;
&lt;br /&gt;
Давайте создадим пустую таблицу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 GtkTextTagTable *tags_table = gtk_text_tag_table_new ();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Затем создадим тэг, которым будем подсвечивать ошибочные слова:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 GtkTextTag *tag_spell_err = gtk_text_tag_new (“spell_err”);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание – мы даем тэгу имя '''spell_err''', чтобы потом иметь&lt;br /&gt;
возможность обратиться к нему. Назначим тэгу следующие свойства:&lt;br /&gt;
цвет букв (переднего плана, '''foreground''') и подчеркивание ('''underline'''):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 g_object_set (G_OBJECT (tag_spell_err), “foreground”, “red”, NULL);&lt;br /&gt;
 g_object_set (G_OBJECT (tag_spell_err), “underline”, PANGO_UNDERLINE_NORMAL, NULL);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В данном примере мы использовали стиль '''PANGO_UNDERLINE_NORMAL''' – обычное подчеркивание прямой линией. Но в библиотеке&lt;br /&gt;
''Pango'', начиная с версии 1.4, появился стиль '''PANGO_UNDERLINE_ERROR''', созданный специально для подчеркивания ошибок (волнистая линия мелким зигзагом). Как написать код, проверяющий версию ''Pango'' и устанавливающий стиль подчеркивания в зависимости от нее?&lt;br /&gt;
А вот так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 #if defined(PANGO_VERSION_CHECK) &amp;amp;&amp;amp; PANGO_VERSION_CHECK(1,4,0)&lt;br /&gt;
 g_object_set (G_OBJECT (tag_spell_err), “underline”, PANGO_UNDERLINE_ERROR, NULL);&lt;br /&gt;
 #else&lt;br /&gt;
 g_object_set (G_OBJECT (tag_spell_err), “underline”, PANGO_ UNDERLINE_SINGLE, NULL);&lt;br /&gt;
 #endif&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь тэг можно поместить в таблицу:&lt;br /&gt;
&lt;br /&gt;
 gtk_text_tag_table_add (tags_table, tag_spell_err);&lt;br /&gt;
&lt;br /&gt;
Создадим отдельный текстовый буфер с указанной таблицей тэгов:&lt;br /&gt;
&lt;br /&gt;
 GtkTextBuffer *text_buffer = gtk_text_buffer_new (tags_table);&lt;br /&gt;
&lt;br /&gt;
У '''GtkTextView''' уже есть буфер по умолчанию. Заменим его на новый&lt;br /&gt;
буфер с нашей таблицей тэгов:&lt;br /&gt;
&lt;br /&gt;
 gtk_text_view_set_buffer (text_view, text_buffer);&lt;br /&gt;
&lt;br /&gt;
Альтернативный вариант вариант – не создавать свой буфер и таблицу, а получить указатель на уже существующий буфер с помощью&lt;br /&gt;
функции '''gtk_text_buffer_get_tag_table()''', и добавить тэг в полученную&lt;br /&gt;
таблицу. Решение зависит от вас и от архитектуры вашей программы.&lt;br /&gt;
&lt;br /&gt;
Приведем код, отвечающий непосредственно за проверку содержимого текстового буфера на ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 GtkTextIter iter;&lt;br /&gt;
 GtkTextIter a;&lt;br /&gt;
 GtkTextIter b;&lt;br /&gt;
 gchar *p = NULL;&lt;br /&gt;
 gchar *text;&lt;br /&gt;
 gtk_text_buffer_get_iter_at_offset (text_buffer, &amp;amp;iter, 0);&lt;br /&gt;
 if (gtk_text_iter_starts_word (&amp;amp;iter))&lt;br /&gt;
      a = iter;&lt;br /&gt;
 do&lt;br /&gt;
   {&lt;br /&gt;
     if (gtk_text_iter_starts_word (&amp;amp;iter))&lt;br /&gt;
        {&lt;br /&gt;
          b = iter;&lt;br /&gt;
          if (gtk_text_iter_backward_char (&amp;amp;b))&lt;br /&gt;
              a = iter;&lt;br /&gt;
          if (gtk_text_iter_forward_word_end (&amp;amp;iter))&lt;br /&gt;
              if (gtk_text_iter_ends_word (&amp;amp;iter))&lt;br /&gt;
                 {&lt;br /&gt;
                   text = gtk_text_iter_get_slice (&amp;amp;a, &amp;amp;iter);&lt;br /&gt;
                   if (text)&lt;br /&gt;
                      {&lt;br /&gt;
                        if (g_utf8_strlen (text, -1) &amp;gt; 1)&lt;br /&gt;
                           {&lt;br /&gt;
                             if (! aspell_speller_check (text_buffer, text, -1))&lt;br /&gt;
                                {&lt;br /&gt;
                                  gtk_text_buffer_apply_tag (text_buffer, gtk_text_tag_&lt;br /&gt;
 table_lookup(gtk_text_buffer_get_tag_table (text_buffer), “spell_err”), &amp;amp;a, &amp;amp;iter);&lt;br /&gt;
                                    g_free (text);&lt;br /&gt;
                                    continue;&lt;br /&gt;
                                  }&lt;br /&gt;
                             }&lt;br /&gt;
                          g_free (text);&lt;br /&gt;
                        }&lt;br /&gt;
                   }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
 while (gtk_text_iter_forward_char (&amp;amp;iter));&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Неплохо, правда? Вначале мы получаем итератор '''iter''', указывающий на начало буфера. Также у нас есть вспомогательные итераторы&lt;br /&gt;
'''a''' и '''b''', которых мы используем для более точного последовательного&lt;br /&gt;
перебора букв в буфере. Перебирая в цикле один символ за другим,&lt;br /&gt;
мы получаем слова. Текст каждого слова помещаем в переменную '''text''' и передаем эту переменную в функцию '''aspell_speller_check()'''. Если&lt;br /&gt;
слова нет в словаре, то применяем цветовой тэг к участку в буфере,&lt;br /&gt;
отмеченному итераторами, которые ограничивают текущее слово. Тэг&lt;br /&gt;
применяется с помощью функции '''gtk_text_buffer_apply_tag()'''. Этой&lt;br /&gt;
функции передаются следующие параметры: '''text_buffer''' – текстовый&lt;br /&gt;
буфер, указатель на тэг. Его мы извлекаем по имени из таблицы тэгов,&lt;br /&gt;
которая назначена данному буферу. Делается это так:&lt;br /&gt;
&lt;br /&gt;
 gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table (text_buffer), “spell_err”)&lt;br /&gt;
&lt;br /&gt;
Оставшиеся два параметра – итераторы, задающие начало и конец&lt;br /&gt;
отмечаемого тэгом участка в буфере. Вот вам задача для самостоятельного решения: перед вызовом кода подчеркивания ошибок надо&lt;br /&gt;
убрать возможное прежнее подчеркивание, ведь есть вероятность, что&lt;br /&gt;
пользователь уже проверял орфографию, а затем исправил некоторые&lt;br /&gt;
слова. Поэтому, прежде чем заново подчеркивать ошибки, найдите в&lt;br /&gt;
буфере все тэги с именем “'''spell_err'''” и удалите их.&lt;br /&gt;
&lt;br /&gt;
====Помощь зала====&lt;br /&gt;
&lt;br /&gt;
Помимо того, что ''Aspell'' может сообщить вам о наличии или отсутствии&lt;br /&gt;
слова в словаре, он также дает список возможных вариантов верного&lt;br /&gt;
написания предложенного слова. В приведенном ниже коде предполагается, что ошибочное слово хранится в переменной '''error_word''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 AspellWordList *suggestions = aspell_speller_suggest (speller, error_word, -1);&lt;br /&gt;
 if (! suggestions)&lt;br /&gt;
    ; //например, выходим по return&lt;br /&gt;
 //иначе получаем список предположений:&lt;br /&gt;
 AspellStringEnumeration *elements = aspell_word_list_elements&lt;br /&gt;
 (suggestions);&lt;br /&gt;
 const char *word;&lt;br /&gt;
 //и перебирая их (elements) по одному, печатаем в консоль:&lt;br /&gt;
 while (word = aspell_string_enumeration_next (elements))&lt;br /&gt;
        printf (“%s \n”, word);&lt;br /&gt;
 //удаляем объект:&lt;br /&gt;
 delete_aspell_string_enumeration (elements);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Не буду вдаваться в подробности внутреннего устройства этих&lt;br /&gt;
функций – скажу лишь, что API можно было сократить наполовину.&lt;br /&gt;
Память, полученную от '''aspell_speller_suggest()''', освобождать не нужно – возвращается указатель на '''const'''. Объект доступен до следующего&lt;br /&gt;
вызова вышеупомянутой функции.&lt;br /&gt;
&lt;br /&gt;
Вот мы и рассмотрели основные функции ''Aspell''. Напоследок расскажу еще об одной, которой программисты пользуются очень часто –&lt;br /&gt;
это добавление нового слова в словарь. Пример очевиден:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 aspell_speller_add_to_personal (speller, word, strlen (word));&lt;br /&gt;
 aspell_speller_save_all_word_lists (speller);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Размер слова указывается в байтах, поэтому годится обычная '''strlen''', даже если слово у вас хранится в UTF-8. И не забывайте вызывать '''aspell_speller_save_all_word_lists()''', иначе слово хотя и добавитсяв текущую сессию проверки орфографии, но не будет сохранено в&lt;br /&gt;
словарном файле.&lt;br /&gt;
&lt;br /&gt;
===ЧАСТЬ 2: ''Enchant''===&lt;br /&gt;
&lt;br /&gt;
''Enchant'' – побочный продукт разработчиков ''AbiWord'' (http://www.abisource.com/enchant). Собственно говоря, это и не движок сам по себе, а&lt;br /&gt;
программная прослойка, предоставляющая доступ к другим движкам&lt;br /&gt;
проверки орфграфии, как-то: ''Aspell, ISpell, MySpell, Uspell, Hspell, AppleSpell'' и ''Hunspell''. Разработчики позаботились об удобном подключении библиотеки при компиляции – то бишь предоставили пакет&lt;br /&gt;
для ''pkg-config''. Поэтому, чтобы проверить наличие библиотеки и подключить ее к своей программе, надо добавить в '''configure.in''' примерно&lt;br /&gt;
следующее:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
 echo -n “checking for enchant... “&lt;br /&gt;
 if pkg-config --exists enchant ; then&lt;br /&gt;
              LIBS=”$LIBS `pkg-config --libs enchant `”&lt;br /&gt;
              CFLAGS=”$CFLAGS `pkg-config --cflags enchant `”&lt;br /&gt;
              AC_DEFINE(ENCHANT_SUPPORTED, 1, [ENCHANT_&lt;br /&gt;
 SUPPORTED])&lt;br /&gt;
              echo “yes”&lt;br /&gt;
 else&lt;br /&gt;
              echo “no”&lt;br /&gt;
 fi&lt;br /&gt;
        Ну, а в коде программы:&lt;br /&gt;
 #ifdef ENCHANT_SUPPORTED&lt;br /&gt;
 #include “enchant.h”&lt;br /&gt;
 #endif&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Общение с ''Enchant'' происходит в кодировке UTF-8, вам не нужно указывать это напрямую. Инициализация словаря выполняется&lt;br /&gt;
просто:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 EnchantBroker *broker = enchant_broker_init ();&lt;br /&gt;
 EnchantDict *dict = NULL;&lt;br /&gt;
 gchar *lang = g_getenv (“LANG”);&lt;br /&gt;
 if (lang)&lt;br /&gt;
    dict = enchant_broker_request_dict (broker, lang);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вначале создается брокер, у которого запрашиваем словарь: брокер ими заведует. В приведенном выше примере мы просим у брокера:&lt;br /&gt;
«Дай нам словарь для языка текущей локали». Но ведь известно, что&lt;br /&gt;
''Enchant'' поддерживает одновременно несколько движков проверки&lt;br /&gt;
орфографии. Стало быть, ''Enchant'' даст нам (прозрачно, разумеется)&lt;br /&gt;
тот движок, в котором установлен модуль проверки нужного нам языка. Но что если такой модуль есть для нескольких движков? Например,&lt;br /&gt;
для ''Aspell'' и ''MySpell''? Для этих целей существует файл '''/usr/share/Enchant/Enchant.ordering''', в котором задаются приоритеты движков.&lt;br /&gt;
Расположение этого файла у вас может быть иным – всё зависит от&lt;br /&gt;
того, где установлен ''Enchant''. Может также существовать аналогичный&lt;br /&gt;
пользовательский файл, хранящийся в '''~/.enchant'''.&lt;br /&gt;
&lt;br /&gt;
Далее, где именно ''Enchant'' ищет словари? Смотря какие. Для&lt;br /&gt;
''MySpell, Ispell'', и ''Uspell'' по умолчанию – в подкаталогах '''/usr/share/enchant''' (опять же, у вас может быть иначе). Например, словарь ''MySpell'' ищется в '''/usr/share/enchant/myspell'''. Что до ''Aspell'', то к нему ''Enchant''&lt;br /&gt;
ищет «общие» словари, без всякой привязки к конкретной установке.&lt;br /&gt;
&lt;br /&gt;
Для проверки слова на правильность написания надо использовать&lt;br /&gt;
следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 int&lt;br /&gt;
 enchant_dict_check (EnchantDict *dict, const char *const word, ssize_tlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Она возвращает '''0''', если слово присутствует в словаре, положительное значение – если слова там нет, и отрицательное в случае внутренней ошибки движка. Параметры функции: '''dict''' – экземпляр словаря,&lt;br /&gt;
'''word''' – передаваемое для проверки слово (в UTF8), '''len''' – длина этого&lt;br /&gt;
слова в байтах, можно использовать '''strlen''' либо значение '''-1''', если строка завершается нулем.&lt;br /&gt;
&lt;br /&gt;
Получение списка предположительно верных написаний слова:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 size_t out_n_suggs;&lt;br /&gt;
 gchar **words = enchant_dict_suggest (dict, s, -1, &amp;amp;out_n_suggs);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь слова-предположения помещаются в строковой массив&lt;br /&gt;
'''words'''. Количество элементов массива возвращается в переменной&lt;br /&gt;
'''out_n_suggs'''. А чтобы освободить память, отведенную под этот массив,&lt;br /&gt;
нужно сделать так:&lt;br /&gt;
&lt;br /&gt;
 enchant_dict_free_string_list (dict, words);&lt;br /&gt;
&lt;br /&gt;
Наконец, после работы с брокером и словарем нужно тоже очищать память:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 if (dict)&lt;br /&gt;
    enchant_broker_free_dict (broker, dict);&lt;br /&gt;
 if (broker)&lt;br /&gt;
    enchant_broker_free (broker);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Легко видеть, что API у ''Enchant'' более простое, чем у ''Aspell''.&lt;br /&gt;
Использование того или иного движка зависит от программы. Мне&lt;br /&gt;
кажется, что наилучшее решение – это поддержка сразу нескольких&lt;br /&gt;
движков в зависимости от того, какие из них установлены. Собственно,&lt;br /&gt;
этим и занимается ''Enchant'', но ''Aspell'' более традиционен для Linux и&lt;br /&gt;
работает «из коробки» в большинстве дистрибутивов. Кроме того,&lt;br /&gt;
даже при наличии ''Enchant, Aspell'' с его 70 словарями наверняка будет&lt;br /&gt;
использоваться тем же ''Enchant'' в качестве движка. '''LXF'''&lt;/div&gt;</description>
			<pubDate>Mon, 06 Apr 2009 09:39:47 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF100-101:%D0%A3%D1%80%D0%BE%D0%BA_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B8</comments>		</item>
	</channel>
</rss>