В этом фрагменте кода, foo.c, иллюстрируется способ создания программы, не зависящей от применяемого кодового набора.
Ниже приведен пример исходного файла сообщений для утилиты foo. В этом каталоге содержится всего три сообщения. Обычно в программах применяются сходные сообщения.
Ниже приведен исходный файл сообщений для программы foo - foo.msg.
$quote "
$set MS_FOO
CANTOPEN "foo: невозможно открыть %s\n"
BYTECNT "число байт: %d\n"
CHARCNT "число символов: %d
Для создания каталога сообщений, к которому можно обращаться во время выполнения, введите команду runcat:
runcat foo foo.msg
В результате работы команды будет создан приведенный ниже файл foo_msg.h. Обратите внимание, что имя набора сообщений - MS_FOO, а имена сообщений - CANTOPEN, BYTECNT и CHARCNT. Эти обозначения применяются дальше в программе.
/* ** Файл заголовка: foo_msg.h */
#ifndef _H_FOO_MSG #define _H_FOO_MSG #include <limits.h> #include <nl_types.h> #define MF_FOO "foo.cat" /* Следующие строки были созданы на основе wc.msg. */ /* Определения для набора MS_FOO */ #define MS_FOO 1 #define CANTOPEN 1 #define BYTECNT 2 #define CHARCNT 3 #endif
Термин универсальный способ обработки применяется по отношению к приложениям, в которых для обработки однобайтовых и многобайтовых символов применяются одни и те же функции. Такой способ позволяет создавать программы, не зависящие от локали, без применения команд препроцессора ifdef. Все символы обрабатываются одинаково, независимо от того, к какому кодовому набору они принадлежат.
Такой подход предпочтительнее, но он может негативно повлиять на производительность. Из-за этого его не рекомендуется применять в общем случае. Однако если производительность программы не снижается, рекомендуется применять универсальный способ обработки.
Ниже приведена версия утилиты foo, не зависящая от локали и обрабатывающая все кодовые наборы единым образом:
/* * ИМЯ_КОМПОНЕНТА: * * ФУНКЦИИ: foo * * Эта программа подсчитывает число байт и * число символов в текстовом файле. * * Эта программа приведена только в качестве примера. * Возможна дальнейшая оптимизация кода. * */
#include <stdio.h> #include <ctype.h> #include <locale.h> #include <stdlib.h> #include "foo_msg.h"
#define MSGSTR(Num,Str) catgets(catd,MS_FOO,Num,Str)
/* * ИМЯ: foo * * ФУНКЦИИ: Подсчитывает число символов в файле. * */
main (argc,argv) int argc; char **argv; { int bytesread, /* число считанных байт */ bytesprocessed; int leftover;
int i; int mbcnt; /* число байт в символе */ int f; /* файловый дескриптор */ int mb_cur_max; int bytect; /* счетчики байт */ int charct; /* и символов */ char *curp, *cure; /* текущий и конечный указатели в буфере */ char buf[BUFSIZ+1];
nl_catd catd;
wchar_t wc;
/* Получить текущее значение локали */ (void) setlocale(LC_ALL,"");
/* После установки локали открыть каталог сообщений */ catd = catopen(MF_FOO,NL_CAT_LOCALE);
/* Проанализировать аргументы, если есть. */
/* ** Определить максимальное число байт в символе ** текущей локали. */ mb_cur_max = MB_CUR_MAX; i = 1;
/* Открыть указанный файл и вывести сообщения при наличии ошибок */ f = open(argv[i],0); if(f<0){ fprintf(stderr,MSGSTR(CANTOPEN, /*MSG*/ "foo: невозможно открыть %s\n"), argv[i]); /*MSG*/ exit(2); }
/* Инициализировать переменные-счетчики */ bytect = 0; charct = 0;
/* Начать подсчет байт и символов */
leftover = 0; for(;;) { bytesread = read(f,buf+leftover, BUFSIZ-leftover); /* при необходимости, вывести сообщения об ошибках */ if(bytesread <= 0) break;
buf[leftover+bytesread] = '\0'; /* Обеспечить чтение символа полностью */ bytect += bytesread; curp=buf; cure = buf + bytesread+leftover; leftover=0; /* Остаток пуст */
for(; curp<cure ;){ /* Преобразовать к широкому символу */ mbcnt= mbtowc(&wc, curp, mb_cur_max); if(mbcnt <= 0){ mbcnt = 1; }else if (cure - curp >=mb_cur_max){ wc = *curp; mbcnt =1; }else{ /* Требуются дополнительные данные */ leftover= cure - curp; strcpy(buf, curp, leftover); break; } curp +=mbcnt; charct++; } }
/* Вывести число символов и байт */ fprintf(stderr,MSGSTR(BYTECNT, "число байт:%d\n"), bytect); fprintf(stderr,MSGSTR(CHARCNT, "число символов:%d\n"), charct); close(f); exit(0);
Термин единый исходный текст с двумя способами обработки относится к приложению, в котором существует два способа обработки. На этапе выполнения выбирается один из способов в зависимости от того, используется в текущей локали однобайтовый или многобайтовый кодовый набор.
Единый исходный файл с двумя способами обработки следует создавать в том случае, если это позволяет сохранить высокую производительность без значительного увеличения размера исполняемого файла. Исходя из особенностей приложения, оцените увеличение объема исполняемого файла.
В программе, использующей этот способ, макроопределение MB_CUR_MAX определяет максимальное число байт в многобайтовом символе текущей локали. Оно позволяет выбрать способ обработки на этапе выполнения. Для этого применяется логический флаг:
int mbcodeset ; /* После выполнения setlocale(LC_ALL,"") ** выбирается путь выполнения. */ if(MB_CUR_MAX == 1) mbcodeset = 0; else mbcodeset = 1;
В данном случае проверяется текущий кодовый набор, и если он оказывается многобайтовым, устанавливается флаг mbcodeset. Проверка этого флага выполняется значительно быстрее, чем многократная проверка значения макроопределения MB_CUR_MAX.
if(mbcodeset){ /* Многобайтовые кодовые наборы (однобайтовые наборы ** также поддерживаются) */ /* Применяются функции обработки многобайтовых или широких символов */ }else{ /* Однобайтовые кодовые наборы */ /* Обработка с использованием соответствующих функций */ }
Такой подход может применяться в том случае, когда поддержка различных языков занимает небольшую часть модуля. Дополнительные проверки для выбора способа обработки могут негативно сказаться на производительности. При разработке программы не забывайте, что подобные проверки не должны выполняться слишком часто.
Приведенная ниже версия утилиты foo создает один объект, при этом исполняемая ветвь программы определяется на этапе выполнения. Обратите внимание, что различаются только однобайтовые и многобайтовые наборы.
/* * ИМЯ_КОМПОНЕНТА: * * ФУНКЦИИ: foo * * Эта программа подсчитывает число байт и * число символов в текстовом файле. * * Эта программа приведена только в качестве примера. * Возможна дальнейшая оптимизация кода. * */
#include <stdio.h> #include <ctype.h> #include <locale.h> #include <stdlib.h> #include "foo_msg.h"
#define MSGSTR(Num,Str) catgets(catd,MS_FOO,Num,Str)
/* * ИМЯ: foo * * ФУНКЦИИ: Подсчитывает число символов в файле. * */
main (argc,argv) int argc; char **argv; { int bytesread, /* число считанных байт */ bytesprocessed; int leftover;
int i; int mbcnt; /* число байт в символе */ int f; /* файловый дескриптор */ int mb_cur_max; int bytect; /* счетчики байт */ int charct; /* и символов */ char *curp, *cure; /* текущий и конечный указатели буфера */ char buf[BUFSIZ+1];
nl_catd catd;
wchar_t wc;
/* флаг, обозначающий, что текущий ** кодовый набор - многобайтовый */ int multibytecodeset;
/* Получить текущее значение локали */ (void) setlocale(LC_ALL,"");
/* После установки локали открыть каталог сообщений */ catd = catopen(MF_FOO,NL_CAT_LOCALE);
/* Проанализировать аргументы, если есть. */
/* ** Определить максимальное число байт в символе ** текущей локали. */ mb_cur_max = MB_CUR_MAX;
if(mb_cur_max >1) multibytecodeset = 1; else multibytecodeset = 0;
i = 1;
/* Открыть указанный файл и вывести сообщения при наличии ошибок */ f = open(argv[i],0); if(f<0){ fprintf(stderr,MSGSTR(CANTOPEN, /*MSG*/ "foo: невозможно открыть %s\n"), argv[i]); /*MSG*/ exit(2); }
/* Инициализировать переменные-счетчики */ bytect = 0; charct = 0;
/* Начать подсчет байт и символов */
leftover = 0; if(multibytecodeset){ /* Полная поддержка национальных языков */ /* Обрабатываются все многобайтовые кодовые наборы */ for(;;) { bytesread = read(f,buf+leftover, BUFSIZ-leftover); /* при необходимости, вывести сообщения об ошибках */ if(bytesread <= 0) break; buf[leftover+bytesread] = '\0'; /* Обеспечить чтение символа полностью */ bytect += bytesread; curp=buf;
cure = buf + bytesread+leftover; leftover=0; /* Остаток пуст */ for(; curp<cure ;){ /* Преобразовать к широкому символу */ mbcnt= mbtowc(&wc, curp, mb_cur_max); if(mbcnt <= 0){ mbcnt = 1; }else if (cure - curp >=mb_cur_max){ wc = *curp; mbcnt =1;
}else{ /* Требуются дополнительные данные */ leftover= cure - curp; strcpy(buf, curp, leftover); break; } curp +=mbcnt; charct++; } } }else {
/* Код для однобайтовых наборов ** Преобразования к широкому формату не выполняется, за счет чего ** при работе с однобайтовыми наборами повышается производительность */
for(;;) { bytesread = read(f,buf, BUFSIZ); /* при необходимости, вывести сообщения об ошибках */ if(bytesread <= 0) break;
bytect += bytesread; charct += bytesread; }
}
/* Вывести число символов и байт */ fprintf(stderr,MSGSTR(BYTECNT, "число байт:%d\n"), bytect); fprintf(stderr,MSGSTR(CHARCNT, "число символов:%d\n"), charct); close(f); exit(0);