Текст книги "Linux программирование в примерах"
Автор книги: Арнольд Роббинс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 43 (всего у книги 55 страниц)
В самые ранние дни вычислительной техники различные системы назначали различные соответствия между числовыми значениями и глифами (glyphs) – символами, такими, как буквы, цифры и знаки пунктуации, используемыми для общения с людьми. В конечном счете появились два широко использующихся стандарта: кодировка EBCDIC, используемая IBM и подобными ей мэйнфреймами, и ASCII, использующаяся для всего остального. Сегодня, за исключением мэйнфреймов, ASCII является основой для всех других использующихся наборов символов.
Оригинальный семиразрядный набор символов ASCII достаточен для американского английского и большинства знаков пунктуации и специальных символов (таких, как $
, но нет символа для «цента»). Однако, имеется много языков и много стран, которым нужны другие наборы символов. ASCII не оперирует версиями романских символов с надстрочными значками, использующимися в Европе, а во многих азиатских языках тысячи символов. Для устранения этих недостатков были разработаны новые технологии.
Литература по интернационализации изобилует ссылками на три фундаментальных термина. Определив их и взаимоотношения между ними, мы сможем представить общее описание соответствующих функций API С.
Набор символов (character set)
Определение значений, присваиваемых различным целым величинам; например того, что A равно 65. Любой набор символов, использующий более восьми битов на символ, называется многобайтным набором символов.
Представление набора символов (character set encoding)
ASCII использует для представления символов один байт. Таким образом, целое значение хранится само по себе, непосредственно в дисковых файлах. Более современные наборы символов, особенно различные версии Unicode[150]150
http://www.unicode.org
– Примеч. автора.
[Закрыть], используют для представления символов 16-разрядные или даже 32-разрядные целые значения. Для большинства определенных символов один, два или даже три старших байта целого значения равны нулю, что делает непосредственное хранение таких значений на диске неэффективным. Представление набора символов описывает механизм для преобразования 16– или 32-разрядных значений в последовательности от одного до шести байтов для сохранения на диске таким образом, что в целом наблюдается значительная экономия дисковой памяти.
Язык
Правила данного языка определяют использование набора символов. В частности, правила влияют на сортировку символов. Например, на французском е, é и è все должны находиться между d и f, независимо от назначенных этим символам числовых значений. Различные языки могут назначить (и назначают) одним и тем же глифам различный порядок сортировки.
Со временем для поддержки многобайтных наборов символов были разработаны различные технологии. Вычислительная практика медленно стремится к Unicode и соответствующим его представлениям, но стандартный С и POSIX поддерживают как старые, так и современные технологии. В данном разделе представлен концептуальный обзор различных возможностей. Нам самим не приходилось их использовать, поэтому мы предпочитаем просто познакомить с ними и предоставить ссылки на дополнительную информацию.
Мы начнем с концепции широких символов (wide character). Широкий символ является целым типом, в котором может храниться любое значение из определенного используемого многобайтного набора символов.
Широкие символы представлены на С типом wchar_t
. C99 предоставляет соответствующий тип wint_t
, в котором может находиться любое значение, допустимое для wchar_t
, а также специальное значение WEOF
, аналогичное обычному EOF
из
. В заголовочном файле
, такие, как iswalnum()
и др., определены в заголовочном файле
.
Широкие символы могут быть от 16 до 32 битов размером в зависимости от реализации. Как упоминалось, они нацелены на манипулирование данными в памяти и обычно не хранятся в файлах непосредственно.
Стандарт C предусматривает для широких символов большое число функций и макросов, соответствующих традиционным функциям, работающим с данными char
. Например, wprintf()
, iswlower()
и т.д. Они документированы в справочных страницах GNU/Linux и в книгах по стандартному С.
Строки широких символов сохраняются на диске путем преобразования их в памяти в многобайтное представление набора символов с последующей записью в дисковый файл. Сходным образом, такие строки считываются с диска через низкоуровневый блочный ввод/вывод, а затем конвертируются в памяти из многобайтной версии в версию широких символов.
Многие описанные кодировки используют для представления многобайтных символов состояния регистра (shift states). Другими словами, в данном потоке байтов значения байтов представляют самих себя до тех пор, пока не встретится специальное управляющее значение. В этот момент интерпретация изменяется в соответствии с текущим состоянием регистра. Таким образом, одно и то же восьмибитовое значение может иметь два значения: одно для обычного состояния, без использования регистра, и другое для использования регистра. Предполагается, что правильно закодированные строки начинаются и заканчиваются с одним и тем же состоянием регистра.
Значительным преимуществом Unicode является то, что его представления являются самокорректирующимися; кодировки не используют состояния регистров, поэтому потеря данных в середине не может повредить последующим закодированным данным.
Первоначальные версии функций преобразования многобайтных символов в широкие и широких в многобайтные поддерживали закрытую копию состояния преобразования (например, состояние регистра, а также все остальное, что могло понадобиться) Такая модель ограничивает использование функции лишь одним видом преобразования в течение жизни программы. Примерами являются mblen()
(определение длины многобайтной строки), mbtowc()
(преобразование многобайтного символа в широкий), wctomb()
(преобразование широкого символа в многобайтный), mbstowcs()
(преобразование многобайтной строки в строку широких символов), wcstombs()
(преобразование строки широких символов в многобайтную строку).
Новые версии этих процедур называются повторно запускаемыми (restartable). Это означает, что код уровня пользователя сохраняет состояние преобразования в отдельном объекте типа mbstate_t
. Соответствующими примерами являются mbrlen()
, mbrtowc()
, wcrtomb()
, mbsrtowcs()
и wcsrtombs()
. (Обратите внимание на r
в их именах, это означает «restartable».)
Языковые проблемы управляются локалью. Ранее в главе мы уже видели setlocale()
POSIX предоставляет продуманный механизм для определения правил, посредством которых работает локаль; некоторые подробности см. в справочной странице GNU/Linux locale(5), а полностью – в самом стандарте POSIX.
Правда в том, что подробности на самом деле не нужны. Вам, как разработчику программ, не нужно беспокоиться о них; как заставить все работать, зависит от разработчиков библиотек. Все, что нужно, это понять концепции и использовать в своем коде соответствующие функции, такие, как strcoll()
(см. раздел 13.2.3 «Сравнение строк: strcoll()
и strxfrm()
»).
Современные системы GLIBC предоставляют отличную поддержку локалей, включая поддерживающие локали процедуры сопоставления регулярных выражений. Например, расширенное регулярное выражение POSIX [[:alpha:]][[:alnum:]]+
соответствует букве, за которой следуют одна или более букв или цифр (алфавитный символ, за которым следуют один или более алфавитно-цифровых символов). Определение того, какие символы соответствуют этим классам, зависит от локали. Например, это регулярное выражение соответствовало бы двум символам 'eè
', тогда как регулярное выражение [a-zA-Z][a-A-Zz0-9]+
традиционного, ориентированного на ASCII Unix – скорее всего нет. Классы символов POSIX перечислены в табл. 13.5.
Таблица 13.5. Классы символов регулярных выражений POSIX
[:alnum:] | Алфавитно-цифровые символы |
[:alpha:] | Алфавитные символы |
[:blank:] | Символы пробела и табуляции. |
[:cntrl:] | Управляющие символы |
[:digit:] | Цифровые символы |
[:graph:] | Символы, являющиеся одновременно печатными и видимыми. (Символ конца строки печатный, но не видимый, тогда как $ является и тем, и другим.) |
[:lower:] | Строчные алфавитные символы |
[:print:] | Печатные (не управляющие) символы |
[:punct:] | Знаки пунктуации (не буквы, цифры, управляющие или пробельные символы) |
[:space:] | Пробельные символы (такие, как сам пробел, символы табуляции, конца строки и т.д) |
[:upper:] | Заглавные алфавитные символы |
[:xdigit:] | Символы из набора abcdefABCDEF0123456789 |
Возможно, вам никогда не придется иметь дело с различными наборами символов и их представлениями. С другой стороны, мир быстро становится «глобальным сообществом», и авторы программ не могут позволить себе быть ограниченными. Следовательно, стоит знать о проблемах интернационализации и наборов символов, а также способах их влияния на поведение вашей системы. По крайней мере, уже один из поставщиков дистрибутивов GNU/Linux устанавливает для систем в Соединенных Штатах локаль по умолчанию en_US.UTF-8
.
1. С, A Reference Manual, 5th edition, by Samuel P. Harbison III and Guy L. Steele, Jr., Prentice-Hall, Upper Saddle River, New Jersey, USA, 2002. ISBN: 0-13-089592-X.
Мы уже упоминали эту книгу раньше. Она дает точное и исчерпывающее описание развития и использования возможностей стандартной библиотеки С для работы с многобайтными и широкими символами. Это особенно ценно для современных систем, поддерживающих C99, поскольку библиотека была значительно усовершенствована для стандарта С 1999 г.
2. GNU gettext tools, by Ulrich Drepper, Jim Meyering, François Pinard, and Bruno Haible. Это руководство по GNU gettext
. На системе GNU/Linux вы можете посмотреть локальную копию через 'info gettext
'. Или загрузить и распечатать последнюю версию (по адресу ftp://ftp.gnu.org/gnu/gettext/
).
• Интернационализация и локализация программ подпадают под общее название поддержки родного языка. Широко распространенными сокращениями являются i18n, l10n и NLS. Центральным является понятие локали, которая позволяет настраивать набор символов, отображение даты, времени, денежных и числовых величин в соответствии с принятыми для данного языка и в данной стране нормами.
• Использование локали устанавливается с помощью функции setlocale()
. Различные категории локали предоставляют доступ к различным видам информации локали. Не использующие локаль программы действуют, как если бы они находились в локали «С», которая выдает типичные для систем Unix до NLS результаты: 7-разрядный ASCII, английские названия месяцев и дней и т.д. Локаль «POSIX» эквивалентна локали «С».
• Сравнение строк с учетом локали осуществляется функцией strcoll()
или комбинацией strxfrm()
и strcmp()
. Возможности библиотеки предоставляют доступ к сведениям о локали (localeconv()
и nl_langinfo()
), а также к специфического для локали форматирования (strfmon()
, strftime()
и printf()
).
• Обратной стороной получения относящейся к локали информации является вывод сообщений на местном языке. Модель catgets()
System V, хотя и стандартизована POSIX, трудна для использования и поэтому не рекомендуется.[151]151
GNU/Linux ее поддерживает, но лишь для совместимости – Примеч. автора.
[Закрыть] Вместо этого GNU gettext
реализует и расширяет оригинальный замысел Solaris.
• При использовании gettext()
оригинальная строка сообщения на английском действует в качестве ключа в двоичном файле перевода, из которого получается перевод строки. Каждое приложение указывает уникальный текстовый домен таким образом, чтобы gettext()
могла найти нужный файл с переводом (известный как «список сообщений»). Текстовый домен устанавливается с помощью функции textdomain()
. При тестировании или иной надобности местоположение списка сообщений можно изменить с помощью функции bindtextdomain()
.
• gettext()
и ее варианты предоставляют доступ к переводам в различных текстовых доменах или различных категориях локалей. Вдобавок, функция ngettext()
и ее варианты дают возможность делать правильные переводы множественных чисел, не перегружая разработчика. Указатель положения в спецификаторе формата printf()
дает возможность перевода форматирующих строк, аргументы которых должны выводиться в другом порядке, чем они располагаются в строке.
• На практике GNU программы используют для пометки переводимых строк в исходных файлах заголовочный файл gettext.h
и макросы _()
и N_()
. Такая практика обеспечивает удобочитаемость исходного кода и возможность его поддержки, предоставляя в то же время преимущества интернационализации и локализации.
• GNU gettext
предоставляет многочисленные инструменты для создания и управления базами данных переводов (переносимых объектных файлов) и их двоичными эквивалентами (объектными файлами сообщений).
• Наконец, стоит быть в курсе проблем наборов символов и их представлений. Поставщики программного обеспечения не могут больше позволить себе предполагать, что их пользователи хотят работать лишь на одном языке.
Упражнения1. Поддерживает ли ваша система локали? Если да, какая локаль используется по умолчанию?
2. Просмотрите справочную страницу locale(1), если она у вас есть. Сколько имеется локалей, если вы посчитаете их с помощью 'locale -a | wc -l
'?
3. Поэкспериментируйте с ch13-strings.с
, ch13-lconv.c
, ch13-strfmon.с
, ch13-quoteflag.c
и ch13-times.c
в различных локалях. Какая из найденных локалей самая «необычная» и почему?
4. Возьмите одну из своих программ. Интернационализируйте ее с использованием GNU gettext
. Постарайтесь найти кого-нибудь, кто говорит на другом языке, чтобы перевести для вас сообщения. Откомпилируйте перевод и протестируйте его, использовав bindtextdomain()
. Какова была реакция вашего переводчика при виде использования перевода?
Глава 14
Расширенные интерфейсы
В данной главе описываются несколько расширенных API. Они сходны по своей природе с описанными ранее в книге или предоставляют дополнительные возможности. Некоторые из них трудно было свободно обсуждать, пока не были освещены предварительные темы.
Порядок представления здесь соответствует порядку глав в первой половине книги. В другом отношении темы не связаны друг с другом. Мы освещаем следующие вопросы: динамическое выделение выровненной памяти; блокировку файлов; ряд функций, работающих со значениями долей секунды; и более развитый набор функций для сохранения и получения произвольных значений данных. Если не указано противное, все API в данной главе включены в стандарт POSIX.
14.1. Выделение выровненной памяти:posix_memalign()
и memalign()
Для большинства задач отлично подходят стандартные процедуры выделения памяти – malloc()
, realloc()
и т.д. Но иногда может понадобиться память, которая выровнена тем или иным способом. Другими словами, адрес первого выделенного байта является кратным какого-нибудь числа. (Например, на некоторых системах копирование памяти осуществляется значительно быстрее, если используются буфера, выровненные по границе слова.) Такую службу предоставляют две функции:
#include
int posix_memalign(void **memptr, size_t alignment, size_t size);
/* POSIX ADV */
void *memalign(size_t boundary, size_t size); /* Обычная */
posix_memalign()
является более новой функцией; она является частью другого необязательного расширения, «Консультативной информации» («Advisory Information»). Работа функции отличается от других функций выделения памяти Linux. При наличии проблемы она не возвращает -1. Вместо этого возвращаемое значение равно 0 при успехе или значению errno
в случае неудачи. Аргументы следующие:
void **memptr
Указатель на переменную void*
. Указываемая переменная будет содержать адрес выделенного блока памяти. Выделенная память освобождается с помощью free()
.
size_t alignment
Требуемое выравнивание. Оно должно быть кратно sizeof(void*)
и быть степенью двойки.
size_t size
Число выделяемых байтов.
memalign()
является нестандартной, но широко доступной функцией, которая работает сходным образом. Возвращаемое значение равно NULL
в случае неудачи и запрошенному блоку памяти при успехе, причем boundary
(степень двойки) обозначает выравнивание, a size
– затребованный размер памяти.
Традиционно выделенная memalign()
память не могла быть освобождена с помощью free()
, поскольку memalign()
использовала для выделения памяти malloc()
и возвращала указатель на выровненный подходящим образом байт где-то внутри блока. Версия GLIBC не имеет этой проблемы. Из этих двух функций следует использовать posix_memalign()
, если она у вас есть.
Современные системы Unix, включая GNU/Linux, дают вам возможность заблокировать часть файла или весь файл для чтения или записи. Подобно многим частям Unix API, которые были разработаны после V7, имеется несколько несовместимых способов осуществить блокировку файлов. Данный раздел рассматривает эти возможности.
Также, как замок на вашей двери предотвращает нежелательные проникновения в ваш дом, блокировка файла предотвращает доступ к данным в файле. Блокировка файлов была добавлена в Unix после разработки V7 (от которой происходят все современные системы Unix), и соответственно в течение некоторого времени в различных системах Unix были доступны и использовались несколько несовместимых механизмов блокировки файлов. Как в BSD Unix, так и в System V были собственные несочетающиеся вызовы для блокировки. В конечном счете POSIX формализовал способ осуществления блокировки файлов System V. К счастью, названия функций в System V и BSD были различны, так что GNU/Linux, в попытке угодить всем, поддерживает обе разновидности блокировок.
Табл. 14.1 суммирует различные виды блокировок.
Таблица 14.1. Функции блокировки файлов
BSD | flock() | √ | √ | √ | ||
POSIX | fcntl() | √ | √ | √ | √ | √ |
POSIX | lockf() | √ | √ | √ | √ | √ |
Имеются следующие аспекты блокировки файлов:
Блокировка записей
Блокировка записи является блокировкой части файла. Поскольку файлы Unix являются просто потоками байтов, было бы корректнее использовать термин блокировка диапазона (range lock), поскольку осуществляется блокировка диапазона байтов. Тем не менее, термин «блокировка записей» общеупотребительный.
Блокировка всего файла
Блокировка всего файла, как предполагает название, блокирует весь файл, даже если его размер меняется в блокированном состоянии. Интерфейс BSD предусматривает блокирование лишь всего файла. Для блокирования всего файла с использованием интерфейса POSIX указывают нулевую длину. Это интерпретируется особым образом как «весь файл».
Блокировка чтения
Блокировка чтения предотвращает запись в читаемую область. В файле может быть несколько заблокированных для чтения участков, даже в одной области файла, не мешающих друг другу, поскольку к данным осуществляется лишь доступ и они не изменяются.
Блокировка записи
Блокировка записи предоставляет исключительный доступ к записываемой области. Если эта область заблокирована также и для чтения, попытка получения блокировки записи либо блокируется, либо завершается неудачей в зависимости от запрошенного типа блокировки. После получения блокировки записи попытка получить блокировку чтения завершается неудачей.
Вспомогательная блокировка
Вспомогательная блокировка (advisory lock) тесно соответствует замку на двери. Говорят, «замки существуют для честных людей», что означает, что если кто-нибудь на самом деле захочет вломиться в ваш дом, он, возможно, найдет способ это сделать, несмотря на наличие замка в двери. То же и со вспомогательной блокировкой; она работает лишь тогда, когда тот, кто пытается получить доступ к заблокированному файлу, сначала пытается получить блокировку. Однако, программа может совершенно игнорировать вспомогательные блокировки и делать с файлом, что захочет (конечно, пока это разрешается правами допуска файла).
Обязательная блокировка
Обязательная блокировка является более строгой: когда установлена обязательная блокировка, ни один другой процесс не может получить доступ к заблокированному файлу. Любой процесс, который пытается игнорировать это, либо сам блокируется до снятия блокировки файла, либо его попытка завершится неудачей. (Под GNU/Linux по крайней мере это включает root
!)
Вспомогательная блокировка достаточна для взаимодействующих программ, разделяющих индивидуальный файл, когда не предполагается использование этого файла ни одним другим приложением. Обязательная блокировка подходит в ситуации, когда избежание конфликта в использовании файла является критическим, как в коммерческих системах баз данных.
POSIX стандартизует лишь вспомогательную блокировку. Обязательная блокировка доступна на GNU/Linux, а также в ряде коммерческих систем Unix, но детали варьируют. Далее в данном разделе мы рассмотрим детали для GNU/Linux.