📄 2.unicode简介.txt
字号:
有文字模式、命令列C语言程序写作历史的程序写作者往往特别喜欢printf函数。即使可以使用更简单的命令(例如puts),但printf出现在Kernighan和Ritchie的「hello, world」程序中一点也不会令人惊奇。我们知道,增强后的「hello, world」最终还是需要printf的格式化输出,因此我们最好从头开始就使用它。
但有个坏消息:在Windows程序中不能使用printf。虽然Windows程序中可以使用大多数C的执行时期链接库-实际上,许多程序写作者更愿意使用C内存管理和文件I/O函数而不是Windows中等效的函数-Windows对标准输入和标准输出没有概念。在Windows程序中可使用fprintf,而不是printf。
还有一个好消息,那就是仍然可以使用sprintf及sprintf系列中的其它函数来显示文字。这些函数除了将内容格式化输出到函数第一个参数所提供的字符串缓冲区以外,其功能与printfI相同。然后便可对该字符串进行操作(例如将其传给MessageBox)。
如果您从未使用过sprintf (我第一次开始写Windows程序时也没用过此函数),这里有一个简短的执行实体,printf函数说明如下:
int printf (const char * szFormat, ...) ;
第一个参数是一个格式字符串,后面是与格式字符串中的代码相对应的不同类型多个参数。
sprintf函数定义如下:
int sprintf (char * szBuffer, const char * szFormat, ...) ;
第一个参数是字符缓冲区;后面是一个格式字符串。Sprintf不是将格式化结果标准输出,而是将其存入szBuffer。该函数返回该字符串的长度。在文字模式程序设计中,
printf ("The sum of %i and %i is %i", 5, 3, 5+3) ;
的功能相同于
char szBuffer [100] ;
sprintf (szBuffer, "The sum of %i and %i is %i", 5, 3, 5+3) ;
puts (szBuffer) ;
在Windows中,使用MessageBox显示结果优于puts。
几乎每个人都经历过,当格式字符串与被格式化的变量不合时,可能使printf执行错误并可能造成程序当掉。使用sprintf时,您不但要担心这些,而且还有一个新的负担:您定义的字符串缓冲区必须足够大以存放结果。Microsoft专用函数_snprintf解决了这一问题,此函数引进了另一个参数,表示以字符计算的缓冲区大小。
vsprintf是sprintf的一个变形,它只有三个参数。vsprintf用于执行有多个参数的自订函数,类似printf格式。vsprintf的前两个参数与sprintf相同:一个用于保存结果的字符缓冲区和一个格式字符串。第三个参数是指向格式化参数数组的指针。实际上,该指针指向在堆栈中供函数呼叫的变量。va_list、va_start和va_end宏(在STDARG.H中定义)帮助我们处理堆栈指针。本章最后的SCRNSIZE程序展示了使用这些宏的方法。使用vsprintf函数,sprintf函数可以这样编写:
int sprintf (char * szBuffer, const char * szFormat, ...)
{
int iReturn ;
va_list pArgs ;
va_start (pArgs, szFormat) ;
iReturn = vsprintf (szBuffer, szFormat, pArgs) ;
va_end (pArgs) ;
return iReturn ;
}
va_start宏将pArg设置为指向一个堆栈变量,该变量地址在堆栈参数szFormat的上面。
由于许多Windows早期程序使用了sprintf和vsprintf,最终导致Microsoft向Windows API中增添了两个相似的函数。Windows的wsprintf和wvsprintf函数在功能上与sprintf和vsprintf相同,但它们不能处理浮点格式。
当然,随着宽字符的发表,sprintf类型的函数增加许多,使得函数名称变得极为混乱。表2-1列出了Microsoft的C执行时期链接库和Windows支持的所有sprintf函数。
表2-1
ASCII
宽字符
常规
参数的变数个数
标准版
sprintf
swprintf
_stprintf
最大长度版
_snprintf
_snwprintf
_sntprintf
Windows版
wsprintfA
wsprintfW
wsprintf
参数数组的指针
标准版
vsprintf
vswprintf
_vstprintf
最大长度版
_vsnprintf
_vsnwprintf
_vsntprintf
Windows版
wvsprintfA
wvsprintfW
wvsprintf
在宽字符版的sprintf函数中,将字符串缓冲区定义为宽字符串。在宽字符版的所有这些函数中,格式字符串必须是宽字符串。不过,您必须确保传递给这些函数的其它字符串也必须由宽字符组成。
格式化消息框
程序2-1所示的SCRNSIZE程序展示了如何实作MessageBoxPrintf函数,该函数有许多参数并能像printf那样编排它们的格式。
程序2-1 SCRNSIZE
SCRNSIZE.C
/*---------------------------------------------------------------------------
SCRNSIZE.C -- Displays screen size in a message box
(c) Charles Petzold, 1998
----------------------------------------------------------------------------*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...)
{
TCHAR szBuffer [1024] ;
va_list pArgList ;
// The va_start macro (defined in STDARG.H) is usually equivalent to:
// pArgList = (char *) &szFormat + sizeof (szFormat) ;
va_start (pArgList, szFormat) ;
// The last argument to wvsprintf points to the arguments
_vsntprintf ( szBuffer, sizeof (szBuffer) / sizeof (TCHAR),
szFormat, pArgList) ;
// The va_end macro just zeroes out pArgList for no good reason
va_end (pArgList) ;
return MessageBox (NULL, szBuffer, szCaption, 0) ;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
int cxScreen, cyScreen ;
cxScreen = GetSystemMetrics (SM_CXSCREEN) ;
cyScreen = GetSystemMetrics (SM_CYSCREEN) ;
MessageBoxPrintf ( TEXT ("ScrnSize"),
TEXT ("The screen is %i pixels wide by %i pixels high."),
cxScreen, cyScreen) ;
return 0 ;
}
经由从GetSystemMetrics函数得到的信息,该程序以图素为单位显示了视讯显示的宽度和高度。GetSystemMetrics是一个能用来获得Windows中不同对象的尺寸信息的函数。事实上,我将在第四章用GetSystemMetrics函数向您展示如何在一个Windows窗口中显示和滚动多行文字。
本书与国际化
为国际市场准备的Windows程序不光要使用Unicode。国际化超出了本书的范围,但在Nadine Kano所写的《Developing International Software for Windows 95 and Windows NT》(Microsoft Press,1995年)一书中涉猎了许多。
本书中的程序写作时被限制成既可使用也可不使用定义的UNICODE标识符来编译。这包括对所有字符和字符串定义使用TCHAR,对字符串文字使用TEXT宏,以及注意不要混淆字节和字符。例如,注意SCRNSIZE中的 _vsntprintf呼叫。第二个参数是缓冲区的字符大小。通常,您使用sizeof (szBuffer)。但如果缓冲区中有宽字符,则返回的不是缓冲区的字符长度,而是缓冲区的字节大小。您必须用sizeof(TCHAR)将其分开。
通常,在Visual C++ Developer Studio中,可使用两种不同的设定来编译程序:Debug和Release。为简便起见,对本书的范例程序,我已修改了Debug设定,以便于定义UNICODE标识符。如果程序使用了需要字符串作参数的C链接库函数,那么_UNICODE标识符也在Debug设定中定义(要了解这是在哪里完成的,请从「Project」菜单中选择「Settings」,然后单击「C/C++」标签)。使用这种方式,这些程序就可以方便地被重新编译和连结以供测试。
本书中所有程序-无论是否为Unicode编译-都可以在Windows NT下执行。只有极少数情况例外。本书中按Unicode编译的程序不能在Windows 98中执行,而非Unicode版则可以。本章和第一章的程序就是两个特例。MessageBoxW是Windows 98支持的少数宽字符Windows函数之一。在SCRNSIZE.C中,如果用Windows函数wprintf代替了_vsntprintf(您还必须删除该函数的第二个参数),那么SCRNSIZE.C的Unicode版将不能在Windows 98下执行,这是因为Windows 98不支持wprintfW。
在本书的后面(特别在第六章,介绍键盘的使用时),我们将看到,编写能处理远东版Windows双字符集的Windows程序不是一件容易的事情。本书没有说明如何去做,并且基于这个原因,本书中的某些非Unicode版本的程序在远东版的Windows下不能正常执行。这也是Unicode对将来的程序设计如此重要的一条理由。Unicode允许程序更容易地跨越国界。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -