Кодировки
Если говорить об истории личных взаимоотношений с кодировками, то мой с ними роман начался с первой работы. Год где-нибудь 2002-й, если мне не изменяет память. Тогда ни с линуксом, ни со стандартом POSIX, я был еще не знаком. Ну, по крайней мере вот так что бы по работе. Связано знакомство было прежде всего с веб-разработкой. Сейчас кажется, что нулевые это не такая уж прямо и древность, и то что есть нынче в операционных системах и программировании было уже и тогда. И если подумать, то и действительно во многом так оно и есть.
Юникод в начале нулевых не был чем-то новаторским, большинство браузеров уже тогда понимали его, понимал юникод в целом и Linux. А вот в среде Windows все еще оставались серьезные проблемы. Если сама операционка со времен Windows 2000 и XP в принципе позволяла использовать юникод, то огромное количество программ под неё еще долго находились под влиянием Windows 98 и более старых. Более того, не очень понятно было что делать с файловыми системами. Как сами операционки, так и прикладные программы должны были так или иначе поддерживать FAT32, или даже FAT16, которые все еще использовали ASCII/ANSI для имен файлов.
В общем люди еще долго упирались рогами в землю и использовали родную кодировку Windows, якобы для совместимости со старыми хранилищами и программами. Исключением не стали и программисты, в том числе и веб-разработчки. Что уж говорить, кодовые страницы ASCII используются и по сей день на самых серьезных интернет ресурсах. Поэтому о полном исчезновении этого атавизма говорить преждевременно, разные кодировки встречаются гораздо чаще чем этого бы хотелось.
Исключением не является и ваш покорный говнокодер слуга. Множество исходного кода с локализованными строками и причудливыми способами работы с файлами и базами данных было изготовлено и с моим участием. Иногда было просто легче поступить по-старинке, а порой до сих пор действительно приходилось работать с внешними источниками данных, для "пущей надежности" хранимых в ASCII. Хотя, надо сказать, это всю дорогу мне довольно основательно досаждало. Особенно с тех пор, когда я более внимательно разобрался как с этим всем хозяйством обстоят дела в никсах.
Локали
В POSIX системах оказывалось так, что о кодировках вообще-то позаботились очень основательно и очень задолго до Windows 2000 и даже до вообще появления Юникода. Вводить и распечатывать символы отличные от латиницы разумеется стало нужно и до появления POSIX. Справлялись с этим люди по разному. В основном работа с иностранными алфавитами была вотчиной конкретных программ, таких как текстовые редакторы. В частности этим занимался упомянутый в прошлой заметке emacs. Компиляторам так же самостоятельно предлагалось понимать и отображать разные символы, не поместившиеся в 128 позиций стандартной ASCII.
Когда отдельным программистам надоело каждый раз собирать свои программы под еще один язык (локализовать), был найден совершенно очевидный и рациональный выход - доверить локализацию операционной системе. Что и было немедленно внедрено в стандарт. А частью операционной системы призванной всем этим заниматься обязали команду 'locale' и несколько переменных окружения LANG, LC_ALL, LC_TYPE, LC_MESSAGES и связанную с ними NLSPATH.
Локаль - это куда более широкое понятие, чем просто кодовая страница или язык текста. Создатели стандарта предусмотрительно расширили это понятие вообще до всего, что связано географическим местоположением, где используется система. Это и собственно язык, и разные его особенности, и страна, и валюта, и принятые в государстве единицы измерения, форматы дат, чисел и всё что можно оставить за скобками. Возможно это для кого-то не очень очевидно, но даже функция изменения регистра букв работает для разных языков по разному. Есть языки, в которых два символа обозначают одну фонему. Есть языки, которые пишутся и читаются справа налево, сверху вниз. Добавьте туда всяческие диакритические знаки, диграфы, триграфы, и прочую мыслимую экзотику.
Сама функция 'locale' ничего особенно интересного из себя не представляет. Она просто отображает текущую локаль и разные её параметры. С ключем '-a' выводит все зарегистрированные в системе локали. Изменение локали терминала, или запускаемой программы производится при помощи установки переменных окружения.
$ LANG=en_US date
Выведет текущую дату не в системной локали, а в указанной. В данном случае 'en' - английский язык, 'US' - страна - США. Если указать в качестве страны, скажем, Замбию - 'en_ZM', то можно убедиться, что хоть официальный язык в этой стране и английский, дату замбийские граждане любят указывать чуть по-своему. Для языков с алфавитом, отличающимся от английского, помимо языка и страны после точки нужно указать и кодировку 'ru_RU.utf8'.
Для программиста важен сам принцип работы операционной системы и программ, основанных на локалях. Важно понимать в каком месте и когда в программе можно использовать хардкод, а где значение или формат можно вынести на усмотрение пользователя. Сама программа должна работать независимо от того где и под какой локалью её запустили, даже если для данного языка нет перевода сообщений и интерфейса. Калькулятор калорий должен понимать, что пользователь может ставить точки вместо запятых, что вес здесь в фунтах, а не в килограммах. Вернее даже наоборот, внутреннюю логику программы не должно волновать каким способом и в каких единицах вводятся данные, и какие символы пользователь ввел в качестве своего имени.
С практической точки зрения вы можете, например, протестировать свою программу не отходя от консоли на реальном окружении. А если это реальное окружение использует всё ту же неистребимую 'cp1251', то можно озаботиться тем, что бы локализация вашей программы имела аналог сообщений и интерфейсов для этой кодировки.
Конвертация
После того, как вы вывели все локализованные сообщения и метки полей в вашей программе во внешний файл в юникоде, и теперь вам нужно по требованию упрямого заказчика все это хозяйство перевести в Windows-1251. Или если вам по дороге повстречался чей-то захардкоденный скрипт, который вам лень или некогда приводить к юникоду, то вам может понадобиться следующая стандартная утилита и библиотека - 'iconv'.
Утилита в общем тоже не бог весть какая сложная.
$ iconv -f cp1251 -t utf8 cp1251.txt > utf8.txt
From, To - все понятно. Ключ '-l' показывает все доступные варианты. Можно поиграться с установкой локали перед вызовом, опуская целевую кодировку.
Если читатель еще не забыл содержание предыдущей заметки, то мы остановились на том, что бы открыть и отредактировать в файл в чужой кодировке. Зная теперь как работают локали и кодировки, может возникнуть естественное желание проверить это на практике.
$ LANG=ru_RU.cp1251 vi cp1251.txt
Вроде бы все законно, но в большинстве случаев редактор выведет на экран полную ерунду.
Вся загвоздка в том, что на самом деле действительно все законно, и локаль установлена правильно, и редактор на самом деле работает теперь в указанной кодировке. А вот терминал, под которым запущен редактор, все еще не понимает что он должен отображать. То есть вам либо придется запустить сеанс с соответствующей кодировкой, либо выходить из ситуации как-то по-другому.
И выход есть - 'iconv'. Как и было упомянуто, это не только утилита, но и библиотека. То есть она доступна через API POSIX для других программ системы. А посему используется и прекрасно работает и внутри многих утилит и программ в соответствующей среде. Откроем текст заново.
$ vi cp1251.txt
И введем команду ':e ++enc=cp1251'. Редактор должен самостоятельно открыть файл заново перекодировать его из 'cp1251' в кодировку сеанса. Более того, теперь vi знает, что сам файл должен быть в исходной кодировке и будет при сохранении автоматически вызывать библиотеку 'iconv' и корректно восстанавливать кодировку.
Если надо часто и много открывать файлы в кодировках отличных от системной, можно использовать ключ '-c' исполняющий команду после каждого запуска 'vi', или прописать команду в '.vimrc'.
$ vi -c ":e ++enc=cp1251" cp1251.txt
С 'nano' дела обстоят чуть сложнее, но в большинстве случаев должно сработать нечто подобное.
$ LANG=ru_RU.cp1251 luit -encoding cp1251 nano cp1251.txt
В данном случае 'luit' как-раз подменяет кодировку, используя все тот же 'iconv' для терминала, а не для редактора. Но, в каких-то случаях, это может не сработать. Вариант с 'vi' надежнее.
Проверка орфографии
Изначально я хотел как-то увязать всю эту историю с библиотекой 'aspell', но как-то это по факту у меня не получилось. Если так подумать, то и вещи то это не совсем связанные. Не так уж много и каких-то практических задач, что бы наглядно показать как можно воспользоваться библиотекой непосредственно в операционной системе. Тем более что самый очевидный пример я уже показал в заметке про редакторы.
Наверно, я все-таки пока оставлю этот пункт без внимания. Возможно, где-то потом, например в рубрике "Begin", возникнет повод познакомить читателя и с этим прекрасным изобретением человечества. Надо лишь добавить, что способов использования 'aspell' больше чем только непосредственно проверка орфографии. Это по сути довольно таки полный системный словарь с достаточно шустрым поиском, поэтому способов применения к нему придумать можно на сколько хватит фантазии. Но это как правило никак не связано ни с Linux ни вообще какой-либо операционной системой.
Перекинуть мостик к следующей части серии тоже не получилось. Можно разве что притянуть за уши то факт, что файлы с разными ASCII кодировками чаще всего встречаются в недрах интернета, а с интернетом из консоли Linux можно работать вот так и так.
С другой стороны, в следующей части речь пойдет не столько о том как скачать веб-страницу, сколько о работе в сети в целом, о протоколах, и о том как с ними работать. В протоколе HTTP, кстати, имеется директива касающаяся и кодировок. Вот так можно было еще подвести.
:wq