Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика_1 / C / lecture11 / lecture11

.html
Скачиваний:
11
Добавлен:
09.06.2015
Размер:
18.55 Кб
Скачать

Работа с UNICODE в Windows. Table of Contents 1. Работа с UNICODE (в Windows) 2. Разработка универсальных программ для работы со строками в Windows. 3. Преобразование текстов из UNICODE в ANSI и обратно 4. Задачи. Работа с UNICODE в Windows.   Я уже обсуждал в лекциии 9 проблему использования национальных алфавитов в программах . Использование для представления символов национальных алфавитов однобайтовых переменных типа char привело к появлению большого числа кодовых страниц и к большим неудобствам при использовании программ, содержащих строки с символами из различных национальных алфавитов. Решение проблемы было предложено в 1988 году фирмами Apple и Xerox, предложившими использовать более одного байта для хренения номеров символов. В результате появилась на свет кодировка UNICODE , в которой для хранения номеров символов используется от одного до четырех байтов. В настоящее время практически все ведущие операционные системы, такие как Windows NT, Windows 2000, Windows XP , Linux и другие , переходят на использование кодировки UNICODE. И если, перечисленные выше операционные системы для совместимости со старыми программами , рассчитанными на работы с однобайтовыми символами, продолжают поддержку старых кодовых страниц, то такая новая операционная система , как Windows CE, поддерживает только кодировку UNICODE. В Windows 98 работа с UNICODE представляет массу хлопот и не рекомендуется. В настоящее время в Windows NT, Windows 2000 и Windows XP используется кодировка UNICODE , в которой для хранения номеров символов используются два байта. В стандарт языка C99 введен специальный тип данных для хранения многобайтовых символов - wchar_t. Этот тип данных занимает в Windows два байта, а в Unix - четыре. В настоящей лекции обсуждается работа с UNICODE в операционной системе Windows , хотя с небольшими поправками все сказанное будет справедливо и в LINUX. В двухбайтовой кодировке UNICODE символы с номерами 0x0000 - 0x007f отведены для хранения ASCII кодов, 0x0080 - 0x00ff отведены для хранения символов Latin1, 0x0400 - 0x04ff отведены для хранения Кириллицы. Вот пример программы, работающей с UNICODE строками и записывающей символы 0x0400 - 0x04ff в файл (файл l11-1.c) /*1*/ #include<stdio.h> /*2*/ void main(void){ /*3*/ wchar_t a[256]=L"unicode string"; /*4*/ FILE *str; /*5*/ int i; /*6*/ printf("%S\n",a); /*7*/ str=fopen("unicode_cyr","wb"); /*8*/ for(i=0;i<256;i++){a[i]=(wchar_t)(0x0400+i); /*9*/ fwrite(&a[i],2,1,str); } /*10*/ } /*11*/ В строке 3 объявляется массив a из 256 элементов типа wchar_t и одновременно инициализируется. Большая буква L перед строкой обозначает набор символов UNICODE. Каждый символ строки a занимает в памяти 2 байта. В строке 6 содержимое строки a выводится на экран. Для вывода UNICODE строк используется формат S. В строках 8-9 в цикле for в элементы массива a заносятся номера UNICODE символов, соответствующие русской части таблицы UNICODE (номера символов 0x0400 - 0x04ff) и одновременно эти номера пишутся в файл unicode_cyr. Вот как выглядит это файл в файловом коммандере far при просмотре его , как UNICODE набора. Для работы со строками , состоящими из UNICODE символов используются функции, аналогичные тем, что использовались при работе со строками однобайтовых символов, но в их именах первые символы str заменяются на wcs (от wide character set). Например, для определения длины строки используется функция size_t wcslen( const wchar_t *string );. Cовершенно аналогично определены функции wchar_t *wcsstr( const wchar_t *string, const wchar_t *strCharSet ); для поиска подстроки strCharSet в строке string, wchar_t *wcscat( wchar_t *strDestination, const wchar_t *strSource ); для сшивки двух строк, wchar_t *wcscpy( wchar_t *strDestination, const wchar_t *strSource ); для копирования строки strSource в strDestination и т.д. Все заголовки этих функций объявлены в заголовочном файле string.h. Обращу внимание на две полезные функции , позволяющие писать данные стандартных типов в строки , состоящие из однобайтовых символов и символов UNICODE. Заголовки этих функций объявлены в заголовочном файле stdio.h. int sprintf( char *buffer, const char *format [, argument] ... ); int swprintf( wchar_t *buffer, const wchar_t *format [, argument] ... ); Функция sprintf пишет argument в строку однобайтовых символов buffer по формату , определенному строкой однобайтовых символов format. Функция swprintf пишет argument в UNICODE строку buffer по формату , определенному строкой двухбайтовых (UNICODE) символов format. Вот пример программы , использующей эти функции (файл l11_2.c) /*1*/ #include<stdio.h> /*2*/ void main(void){ /*3*/ char szA[100]; /*4*/ wchar_t szW[100]; /*5*/ //write UNICODE string to ANSI string szA /*6*/ sprintf(szA,"%S",L"UNICODE string to ANSI"); /*7*/ printf("ANSI string szA=%s\n",szA); /*8*/ //write ANSI string to UNICODE string szW /*9*/ swprintf(szW,L"%S","ANSI string to UNICODE"); /*10*/ printf("UNICODE string szW=%S\n",szW); /*11*/ //write int to UNICODE string /*12*/ swprintf(szW,L"%d",105); /*13*/ printf("number = %S\n",szW); /*14*/ /*15*/ //write int to ANSI string /*16*/ sprintf(szA,"%d",105); /*17*/ printf("number = %s\n",szA); /*18*/ /*19*/ //write double to UNICODE string /*20*/ swprintf(szW,L"%e",10.5); /*21*/ printf("number = %S\n",szW); /*22*/ /*23*/ } /*24*/ Обратите внимание , что функция sprintf для записи UNICODE строк использует формат %S , а для записи строк из однобайтовых символов - формат %s. Функция swprintf использует формат %s для записи UNICODE строк формат %S для записи строк из однобайтовых символов. Для преобразования строк в данные различных типов существуют функции (требуются заголовочные файлы stdlib.h и math.h) double atof( const char *string ); - преобразование ANSI строки в double, int atoi( const char *string ); - преобразование ANSI строки в int, long atol( const char *string ); - преобразование ANSI строки в long, int _wtoi( const wchar_t *string ); - преобразование UNICODE строки в int, long _wtol( const wchar_t *string ); - преобразование UNICODE строки в long, double wcstod( const wchar_t *nptr, wchar_t **endptr ); - преобразование UNICODE строки в double, endptr - указатель на символ на котором было прекращено сканирование строки nptr. double strtod( const char *nptr, char **endptr ); - преобразование ANSI строки в double, endptr - указатель на символ на котором было прекращено сканирование строки nptr. Функции strtod и wcstod прекращают сканирование строки nptr на символе , который не может быть конвертирован в число. Вотпример использования функций strtod и wcstod (файл l11_3.с) /*1*/ #include <stdlib.h> /*2*/ #include <stdio.h> /*3*/ /*4*/ void main( void ) /*5*/ { /*6*/ char *string, *stopstring; /*7*/ wchar_t *wstring, *wstopstring; /*8*/ double x; /*9*/ string = "3.1415926This stopped it"; /*10*/ wstring = L"3.1415926This stopped it"; /*11*/ x = strtod( string, &stopstring ); /*12*/ printf( "ANSI string = %s\n", string ); /*13*/ printf(" strtod = %f\n", x ); /*14*/ printf(" Stopped scan at: %s\n\n", stopstring ); /*15*/ x = wcstod( wstring, &wstopstring ); /*16*/ printf( "UNICODE string = %S\n", wstring ); /*17*/ printf(" wcstod = %f\n", x ); /*18*/ printf(" Stopped scan at: %S\n\n", wstopstring ); /*19*/ /*20*/ } /*21*/ Фирма MICROSOFT разработала ряд макросов, которые позволяют писать программы в которых строки могут интерпретироваться компилятором либо , как строки UNICODE, либо , как строки из однобайтовых символов, в зависимости от того определен ли макрос _UNICODE в вашей программе или нет. Но прежде , чем переходить к обсуждению вопроса о написании универсальных программ для работы со строками, т. е. программ в которых строки могут рассматриваться и как однобайтовые и как UNICODE, нам необходимо изучить некоторые директивы препроцессора. Разработка универсальных программ для работы со строками. . Мы уже изучали некоторые простейшие директивы препроцессора, такие как include и define. Напомню , что директивы препроцессора производят обработку текста программы до ее компиляции. Директива всегда начинается с новой строки с символа #. Директива include включает текст файла в программу, директива define определяет макрос. С этими директивами мы знакомились ранее. Сейчас нам потребуется новая директива ifdef. Вот пример текста программы с использованием этой директивы (из заголовочного файла tchar.h) #ifdef _UNICODE typedef wchar_t TCHAR; #else typedef char TCHAR; #endif . Если в вашей программе определен макрос _UNICODE , тогда в вашу программу будет вставлена строка кода typedef wchar_t TCHAR; , а если макрос _UNICODE не определен в программу вставляется строка typedef char TCHAR; . Таким образом если у Вас определен макрос _UNICODE в вашей программе макрос TCHAR будет заменен на wchar_t, а в противном случае на char. Директива ifdef всегда заканчивается директивой endif. Директива else не обязательна. Макрос TCHAR определен в заголовочном файле tchar.h. Вот еще несколько полезных макросов из этого заголовочного файла #ifdef _UNICODE #define __T(x) L##x #else #define __T(x) x #endif Два символа ## - это операция сшивки лексем. Если в вашей программе определен макрос _UNICODE и имеется строка TCHAR a[255]=__T("my string"); , тогда эта строка будет преобразована препроцессором в строку wchar_t a[255]=L"my string". А если вы не определили макрос _UNICODE то в вашей программе будет вставлена строка char a[255]="my string". Для макроса __T(x) в заголовочном файле tchar.h определены два синонима #define _T(x) __T(x) #define _TEXT(x) __T(x) . Для работы со строками в заголовочном файле winnt.h определены макросы PTSTR и PCTSTR #ifdef _UNICODE typedef wchar_t * PTSTR; typedef const wchar_t * PCTSTR; #else typedef char * PTSTR; typedef const char * PCTSTR; #endif . В заголовочном файле tchar.h определены универсальные макросы для всех функций , работающих со строками. Вот небольшой фрагмент этого файла #ifdef _UNICODE #define _tmain wmain #define _tWinMain wWinMain #define _tenviron _wenviron #define __targv __wargv #define _tprintf wprintf #define _tfopen _wfopen #define _tstof _wtof #define _tstol _wtol #define _tstoi _wtoi #define _tcscat wcscat #define _tcschr wcschr #define _tcscpy wcscpy #define _tcscspn wcscspn #define _tcslen wcslen #else #define _tmain main #define _tWinMain WinMain #define _tenviron _environ #define __targv __argv #define _tprintf printf #define _tfopen fopen #define _tstof atof #define _tstol atol #define _tstoi atoi #define _tcscat strcat #define _tcschr strchr #define _tcscpy strcpy #define _tcscspn strcspn #define _tcslen strlen #endif Таким образом фирма MICROSOFT создала очень удобную систему макросов для написания универсальных программ , работающих со строками. В качестве примера применения рассмотренных макросов приведу универсальный вариант программы l10_3.c из предыдущей лекции. Напомню , что эта программа конвертировала строки текстового файла DOS-WINDOWS ( где строки заканчиваются двумя байтами 13 и 10) в строки UNIX ( где строки заканчиваются одним байтом 10 ). Переработанный пример программы ( файл l11_4.c) проводит такую - же конвертацию , но с более широкими возможностями. Если программу компилировать с определенным макросом _UNICODE (первая строка в программе) , то производится конвертация из файла записанного в кодировке UNICODE, результирующий файл тоже пишется в кодировке UNICODE. Если - же закомментировать первую строку программы - работа ведется с файлами содержащими строки из однобайтовых символов. /*1*/ #define _UNICODE /*2*/ #include <stdio.h> /*3*/ #include<tchar.h> /*4*/ int exit(int); /*5*/ void _tmain(int argc,TCHAR *argv[]) { /*6*/ FILE *ptr1,*ptr2; /*7*/ TCHAR c,enter=13; /*8*/ _tprintf(_T("DOS to Unix transformation of text files\n Copyright BVV 1996\n")); /*9*/ if(argc!=3){_tprintf(_T("Error syntax:\n Syntax: unix2dos infile outfile\n")); /*10*/ exit(0); /*11*/ } /*12*/ ptr1=_tfopen(argv[1],_T("rb")); /*13*/ ptr2=_tfopen(argv[2],_T("wb")); /*14*/ while(!feof(ptr1)){ /*15*/ fread(&c,sizeof(TCHAR),1,ptr1); /*16*/ switch(c) { /*17*/ case 13: break; /*18*/ default: fwrite(&c,sizeof(TCHAR),1,ptr2); /*19*/ } /*20*/ } /*21*/ fclose(ptr2); /*22*/ _tprintf(_T("Ok\n")); /*23*/ } /*24*/ Как преобразовать строку UNICODE в ANSI и обратно . В Windows имеется удобная функция для преобразования текстов из кодировки UNICODE в однобайтовую кодировку с заданной кодовой страницей и обратно. Ниже приводится пример программы , которая производит перекодировку файла с UNICODE символами в файл с однобайтовыми символами в определенной кодовой странице. Для перекодировки используется функция WideCharToMultiByte. Для обратного преобразования можно использовать функцию MultiByteToWideChar. Пример приводимой программы хранится в файле l11_5.c , все необходимые комментарии приведены в тексте программы . /*1*/ /* This programm convert text file from unicode to ANSI text file /*2*/ with codepage (866, 1251 ....) */ /*3*/ #include <stdio.h> /*4*/ #include <windows.h> /*5*/ #include <malloc.h> /*6*/ void main(int argv,char *argc[] ) /*7*/ { /*8*/ HANDLE hf1; /*9*/ FILE *outfile; /*10*/ DWORD size,sizer; /*11*/ wchar_t *uni; /*12*/ char *ansi; /*13*/ unsigned int codepage; /*14*/ printf("Unicode to ANSI transformation of text files\n Copyright BVV 2001\n"); /*15*/ if(argv!=4){perror("Error syntax:\n Syntax: unic2ansi infile outfile codepage\n"); /*16*/ } /*17*/ //Open file argc[1] for read only /*18*/ hf1=CreateFile(argc[1],GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); /*19*/ if(hf1==0)perror(strcat("filed open",argc[1])); /*20*/ // get size of file in bytes /*21*/ size=GetFileSize((HANDLE)hf1,NULL); /*22*/ if(size==-1)perror("Error getting file size"); /*23*/ //get memory for buffer /*24*/ uni=malloc(size); /*25*/ // read file hf1 to buffer uni /*26*/ if(!ReadFile((HANDLE)hf1,uni,size,&sizer,NULL))perror("Error reading file"); /*27*/ //to proove Is unicode text file? /*28*/ if(!IsTextUnicode(uni,size,NULL))perror("gm... It is not Unicode Text"); /*29*/ // get memory for buffer ansi for converted text /*30*/ ansi=malloc(size/2); /*31*/ // get codepage /*32*/ codepage=(unsigned int)atoi(argc[3]); /*33*/ // To convert Unicode text from buffer uni to ANSI text to buffer ansi /*34*/ // with codepage codepage /*35*/ WideCharToMultiByte(codepage,0,uni,size/2,ansi,size/2,NULL,NULL); /*36*/ //write result file /*37*/ outfile=fopen(argc[2],"wb"); /*38*/ fwrite(ansi,1,size/2,outfile); /*39*/ fclose(outfile); /*40*/ printf("done"); /*41*/ } /*42*/ /*43*/ В заключение отмечу еще раз, что полная поддержка UNICODE реализована в Windows NT ( c пятым сервиспаком), Windows 2000 , Windows XP и Windows CE. В операционных системах Windows 95, Windows 98 и Windows ME поддержка UNICODE очень слабая и в них нельзя дать грантий правильной работы примеров , приведенных в лекции. Задачи к лекции. 1. Написать программу , которая читает строки из текстового файла на диске, преобразует считанные строки в UNICODE кодировку , используя функцию swprintf и записывает полученные строки в другой файл. Имена файлов должны вводится с командной строки. Для считывания строк из файла использовать функцию fgets, заголовок которой имеет вид char *fgets( char *string, int n, FILE *stream ); . 2. Написать программу , которая читает строки из текстового файла (записанного в кодировке UNICODE) на диске, преобразует считанные строки в однобайтовую кодировку , используя функцию sprintf и записывает полученные строки в другой файл. Имена файлов должны вводится с командной строки. Для считывания строк из файла использовать функцию fgetws, заголовок которой имеет вид wchar_t *fgetws( wchar_t *string, int n, FILE *stream ); . 3. Написать программу , используя макросы Windows из заголовочного файла tchar.h , которая работает либо, как программа из задачи 1, либо как программа из задачи 2, в зависимости от того определен - ли макрос _UNICODE в вашей программе. Универсальный макрос для чтения строк из файла определен в заголовочном файле tchar.h #ifdef _UNICODE #define fgetts fgetws #else #define fgetts fgets . previous next home

Соседние файлы в папке lecture11