[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]

Программирование: Разработка и отладка программ


Пример программы с поддержкой национальных языков

В этом фрагменте кода, foo.c, иллюстрируется способ создания программы, не зависящей от применяемого кодового набора.

Исходный файл сообщений foo

Ниже приведен пример исходного файла сообщений для утилиты foo. В этом каталоге содержится всего три сообщения. Обычно в программах применяются сходные сообщения.

Ниже приведен исходный файл сообщений для программы foo - foo.msg.

$quote "

$set MS_FOO

CANTOPEN        "foo: невозможно открыть %s\n"

BYTECNT         "число байт: %d\n"

CHARCNT         "число символов: %d

Создание файла заголовка с описанием сообщений foo

Для создания каталога сообщений, к которому можно обращаться во время выполнения, введите команду 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);


[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]