社区
英特尔边缘计算技术
帖子详情
Intel的朋友,能否推荐一个高效率的ANSI不区分大小写的检索函数?
danscort2000
2007-03-29 01:19:51
想在一个ANSI 字符串中检索一个子字符串
不区分大小写,也就是大写和小写看作一个字符
目前使用的方法是先拷贝,然后全部转换到小写,再调用strstr来进行检索
感觉效率太低,
有没有高手能提供一个高效率的函数?或者思路也行
不要使用CString string等,全部采用 char 指针进行操作,非常感谢
...全文
376
22
打赏
收藏
Intel的朋友,能否推荐一个高效率的ANSI不区分大小写的检索函数?
想在一个ANSI 字符串中检索一个子字符串 不区分大小写,也就是大写和小写看作一个字符 目前使用的方法是先拷贝,然后全部转换到小写,再调用strstr来进行检索 感觉效率太低, 有没有高手能提供一个高效率的函数?或者思路也行 不要使用CString string等,全部采用 char 指针进行操作,非常感谢
复制链接
扫一扫
分享
转发到动态
举报
AI
作业
写回复
配置赞助广告
用AI写文章
22 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
flyingdog
2007-04-05
打赏
举报
回复
既然你用上了汇编,那就有其他办法提高速度了。
比如字符比较没必要一个一个比。每个字符是8位的,对于搜索字符是4个字符的情况。正好32位。可以进行32位的同时比较。
至于搜索字符不是正好4个字符的情况,也可以想办法。
flyingdog
2007-04-05
打赏
举报
回复
在程序运行时进行大小写转换虽然剩了复制的时间。但是怎么多条件判断会影响cpu的执行性能的。
还是试试使用查表转换的方法。
flyingdog
2007-04-05
打赏
举报
回复
后缀树和后缀数组就算了,实现复杂,常数大。
针对这个数据规模,其他算法可能没什么用。但是KMP的常数应该不大。
贴一个KMP模板。只是模板,不针对这个程序。
#define MAXLEN 1000
int T[MAXLEN];
void kmp_table(char *P){
int i=0;
int j=-1;
char c='\0';
T[0]=j;
while (P[i]!='\0'){
if (P[i]==c){
T[i+1]=j+1;
++j;
++i;
}
else if (j>0){
j=T[j];
}
else {
T[i+1]=0;
++i;
j=0;
}
c = P[j];
}
}
int kmp_search(char *P, char *S){
kmp_table(P);
int m = 0;
int i = 0;
while (S[m+i]!='\0'&&P[i]!='\0') {
if (S[m+i]==P[i]){
++i;
}
else {
m+=i-T[i];
if (i>0)i=T[i];
}
}
if (P[i]=='\0'){
return m;
}
else {
return m+i;
}
}
flyingdog
2007-04-05
打赏
举报
回复
为什么一定要偶数?
比如3个字符的时候,把最高位与成0不是就行了?
flyingdog
2007-04-05
打赏
举报
回复
你前面不是说“而检索子串一般是2到8个字符”??
怎么现在又变成“可能超过16个字符”??
那就先做一个判断,对于短的使用汇编优化掉,对于长的使用KMP。
danscort2000
2007-04-05
打赏
举报
回复
对于是4个字符或者8个字符,这个确实可以优化
问题是这个长度是用户指定的,可能超过16个字符,不一定是偶数
如果要针对性优化,恐怕前面的判断部分和后续的分支判断就需要大量的CPU时间了
所以暂时不用
今天修改了函数,对IO又进行了优化,首先判断长度,用FOR代替了WHILE,这样里面的判断又少了很多
danscort2000
2007-04-05
打赏
举报
回复
现在已经找到方法了,不用ASM汇编,也不用KMP,
目前的逻辑是:
首先计算两个长度
然后对子串全部大写或者小写
用两个FOR代替WHILE[这里可以根据子串的长度优化掉一部分检索]
这样的函数速度比原来汇编的那个还快
danscort2000
2007-04-05
打赏
举报
回复
你前面不是说“而检索子串一般是2到8个字符”??
怎么现在又变成“可能超过16个字符”??
那就先做一个判断,对于短的使用汇编优化掉,对于长的使用KMP。
这个已经说明是一般了,因为这是用户决定的检索关键字
并没有规定最长长度,根据统计资料,一般就是2-8个字符
danscort2000
2007-04-04
打赏
举报
回复
KMP检索函数有了
但是我看了实现
太慢了,这个对全文检索可能意义不错,但是用在我这个场合比我的还慢,
现在用这个实现,如何?
char *AsmStrStrI(char *p1, char *p2) // p1=source
{
char c=0;
while(*p1)
{
__asm
{
mov ecx, p1
mov edx, p2
StartLoop:
mov al, [ecx]
mov ah, [edx]
inc ecx
inc edx
cmp al,0
je EndLoop
cmp ah,0
je EndLoop
cmp al, 'a'
jb Next
cmp al, 'z'
ja Next
and al, 11011111b
Next:
cmp ah, 'a'
jb Compare
cmp ah, 'z'
ja Compare
and ah, 11011111b
Compare:
cmp al, ah
je StartLoop
EndLoop:
mov c, ah
}
if(c==0)
{
return p1;
}
p1++;
}
return NULL;
}
赖勇浩
2007-04-04
打赏
举报
回复
这个肯定是慢了,楼主先实现一个KMP算法吧~
还有一些比KMP更快的算法,你可以找一下相关的ACM论文。
danscort2000
2007-04-04
打赏
举报
回复
现在的优化是,
我在调用前,首先对子串UPCASE
然后传入这个函数
因此没有最前面的那个while循环
danscort2000
2007-04-04
打赏
举报
回复
上面有个错误,加了const ,应该是 char *
danscort2000
2007-04-04
打赏
举报
回复
char * AnsiNocaseStrstr(const char *src,const char *sub)
{
if(*sub==0) return NULL;
if (*src == 0)
{
if (*sub) return NULL;
return src;
}
char c;
size_t i=0;
while(sub[i])
{
if(sub[i] >= 'A' && sub[i] <= 'Z') sub[i] += 0x20;
i++;
}
while (*src)
{
i=0;
while (1)
{
if(sub[i] == 0) return src;
if(src[i]==0) return NULL;
c=src[i];
if(c >= 'A' && c <= 'Z') c += 0x20;
if( sub[i] != c) break;
i++;
}
src++;
}
return NULL;
}
IntelSupport
2007-04-04
打赏
举报
回复
能不能举个一般性能的ANSI不区分大小写的检索函数先
danscort2000
2007-04-03
打赏
举报
回复
哎,看来是没有办法了
等等看,有没有更好的方法
flyingdog
2007-04-01
打赏
举报
回复
的确有PCMPEQB指令,可以进行8个或者16个的同时比较,但是这个并不适合你的要求。因为比较结果是写入mask位,而不是更改flag值。而且无法解决大小写转换问题。具体的指令用法你可以查相应文档。
danscort2000
2007-03-31
打赏
举报
回复
现在是自己写代码了,去掉了内存拷贝转换导致的时间,
大小写直接比较,
由于子串一般都很小,比如2个字符到4个字符,很复杂的算法我不知道是否反而导致低效率
明天继续进行优化,
我想知道是否可以利用MMX啊?
flyingdog
2007-03-31
打赏
举报
回复
如果真要非常注重高效,还是自己写代码比较好。
你使用KMP算法试试,复杂度是O(n+m)。
至于大小写问题,我想到一个方法,不需要先拷贝再转小写,但是不知道实际效果如何,你可以试试。也欢迎大家讨论。
比如对于A和a来说,搜索子串先转成小写。定义一个长度为256的数组T[256]
T['a']='a'
T['A']='a'
T[str[i]]就能在比较时进行转换。
danscort2000
2007-03-30
打赏
举报
回复
只要找到一个就可以了
不需要全部
danscort2000
2007-03-30
打赏
举报
回复
谢谢,不过不想用四string类
因为我有上万个字符串,每个串一般是16个到128个字符
而检索子串一般是2到8个字符
我希望是ANSI检索函数,事实上那个strstr是我自己重新写过的
主要想避免内存分配与拷贝,这个过程太耗费时间,
想将大小写检索都放在一个函数中
其实INTEL 可以开发一个高效率而简短的ANSI检索函数,这个经常要用到的
加载更多回复(2)
ANSI
C文件管理.ppt
文件及文件流
ANSI
C标准文件I/O操作 格式化输入/输出
函数
操作
VC++6.0核心编程源码.rar
第一部分 程序员必读 第一章 对程序错误的处理 在我们开始介绍Microsoft Windows应该提供的许多特性之前,我们首先必须了解Windows的各个
函数
是如何进行错误处理的。 当你调用
一个
Windows
函数
时,它首先要检验你传递给它的的各个参数的有效性,然后再设法执行它的任务。如果你传递了
一个
无效参数,或者由于某种原因它无法执行这项操作,那么该
函数
就会返回
一个
值,指明该
函数
在某种程度上运行失败了。表1-1列出了大多数Windows
函数
使用的数据类型的返回值。 表1-1 Windows
函数
常用的返回值类型 数据类型 表示失败的值 VOID 该
函数
的运行不可能失败。Windows
函数
的返回值类型很少 是VOID。 BOLL 如果
函数
运行失败,那么返回值是0,否则返回的是非0值。最 好对返回值进行测试,以确定它是0还是非0。如果它是TRUE ,则不要测试返回值。 HANDLE 如果
函数
运行失败,则返回值通常是NULL,否则返回值为 HANDLE,,用于标识你可以操作的
一个
对象。对于这个返回 值,你应该小心处理,因为有些
函数
会返回
一个
句柄 值INVALID_HANDLE_VALUE,它被定义为-1。该
函数
的 Platform SDK资料将会清楚地说明该
函数
是返回NULL还 是INVALID_HANDLE_VALID,以便指明
函数
运行已经失败。 PVOID 如果
函数
运行失败,则返回值是NULL,否则返回PVOID,以 标识数据块的内存地址。 LONG/DWORD 这是个难以处理的值。返回数量的
函数
通常返回LONG 或DWORD。如果由于某种原因,
函数
无法对你想要进行计数 的对象进行计数,那么该
函数
通常返回0或-1(根据该
函数
而定) 。如果你调用的
函数
返回了LONG/DWORD,那么请认真阅 读Platform SDK资料,以确保你能正确检查潜在的错误。 当
一个
Windows
函数
返回
一个
错误代码时,它常常可以用来了解
函数
为什么会运行失败。Microsoft公司编译了
一个
所有可能的错误代码的列表,并且为每个错误代码分配了
一个
32位的号码。 从系统内部来讲,当
一个
Windows
函数
检测到
一个
错误时,它会使用
一个
称为线程本地存储器的机制,将相应的错误代码号码与调用的线程关联起来。(“线程本地存储器”将在第21章中介绍)。这将使线程能够互相独立地运行,而不会影响各自的错误代码。当
函数
返回给你时,它的返回值就能指明
一个
错误已经发生。若要确定这是个什么错误,请调用GetLastError
函数
: 见原书P4的程序(1) 该
函数
只返回线程的32位错误代码。 当你拥有32位错误代码的号码时,你必须将该号码转换成更有用的某种对象。WinError.h头文件包含了Microsoft公司定义的错误代码的列表。下面我显示了该列表的某些内容,使你能够看到它的大概样子: 见原书P4的程序(2)和P5的程序 你可以看到,每个错误都有3种表示法:即
一个
消息ID(这是你可以在源代码中使用的
一个
宏,以便与GetLastError的返回值进行比较),消息文本(对错误的英文描述)和
一个
号码(你应该避免使用这个号码,而应该使用消息ID)。请记住,我只选择了WinError.h头文件中的很少一部分内容来向你进行展示,整个文件的长度超过21000行。 当Windows
函数
运行失败时,你应该立即调用GetLastError
函数
,否则,如果你调用另
一个
Windows
函数
,它的值很可能被改写。 说明 GetLastError能返回线程产生的最后
一个
错误。如果该线程调用的Windows
函数
运行成功,那么最后
一个
错误代码就不被改写,并且不指明运行成功。有少 数Windows
函数
并不遵循这一规则,并且它会更改最后的错误代码,但是Platform SDK资料通常指明,当
函数
运行成功时,该
函数
会更改最后的错误代码。 Windows 98 许多Windows 98的
函数
实际上是用Microsoft公司的16位Windows 3.1产 品产生的16位代码来实现的。这种比较老的代码并不通过GetLastError之类函 数来报告错误,而且Microsoft公司并没有在Windows 98中修改16位代码,以 支持这种错误处理方式。对于我们来说,这意味着Windows 98中的许多Win32
函数
在运行失败时不能设置最后的错误代码。该
函数
将返回
一个
值,指明运行失 败,这样你就能够发现该
函数
确实已经运行失败。但是你无法确定运行失败的原 因。 有些Windows
函数
之所以能够成功运行,那是若干个原因产生的结果。例如,创建指明的事件内核对象之所以能够取得成功,原因是你实际上创建了该对象,或者是因为已经存在带有相同名字的事件内核对象。你的应用程序必须知道成功的原因。为了将该信息返回给你,Microsoft公司选择使用最后错误代码机制。这样,当某些
函数
运行成功时,你就能够通过调用GetLadtError
函数
来确定其他的一些信息。对于具有这种行为特性的
函数
来说,Platform SDK资料清楚地说明了GetLastError
函数
可以这样来使用。请参见该资料,以便找出CreateEvent
函数
的例子。 当你进行调试的时候,我发现监控线程的最后错误代码是非常有用的。在Microsoft Visual studio 6.0中,Microsoft的调试程序支持
一个
非常有用的特性,即你可以配置Watch窗口,以便始终都能向你显示线程的最后错误代码的号码和该错误的英文描述。通过选定Watch窗口中的一行,并键入“@err,hr",你就能够做到这一点。观察图1-1,你会看到我已经调用了CreateFile
函数
。该
函数
返回INVALID_HANDLE_VALUE(-1)的HANDLE,表示它未能打开指定的文件。但是Watch窗口向我们显示最后错误代码(即如果我调用GetLastErro
函数
,该
函数
返回的错误代码)是0x00000002。该Watch窗口又进一步指明错误代码2是指“系统不能找到指定的文件。”你会发现它与WinError.h头文件中的错误代码2所指的字符串是相同的。 图1-1 在Visual Studio 6.0的Watch窗口中键入 “@err,hr",你就可以查看当前线程的最后错误代码。 Visual studio还配有
一个
小的实用程序,称为Error Lookup。你可以使用Error Lookup将错误代码的号码转换成它的文本描述。 见P7的Error Lookup插图 如果我在我编写的应用程序中发现
一个
错误,我可能想要向用户显示该错误的文本描述。Windows提供了
一个
函数
,可以将错误代码转换成它的文本描述。该
函数
称为FormatMessage。请看下面的代码: 见原书P8的程序(1) FormatMessage
函数
的功能实际上是非常丰富的,在创建向用户显示的字符串信息时,它是人们首选的
函数
。该
函数
之所以有这样大的作用,原因之一是它很容易用多种语言来进行操作。该
函数
能够检测出用户首选的语言(在Regional Settings Control Panel小应用程序中设定),并返回相应的文本。当然,你首先必须自己转换字符串,然后将已转换的消息表资源嵌入你的.exe文件或DLL模块,不过,这时该
函数
会选定正确的嵌入对象。ErrorShow示例应用程序(本章后面将加以介绍)展示了如何调用该
函数
,以便将Microsoft公司定义的错误代码转换成它的文本描述。 有些人常常问我,Microsoft公司是否建立了
一个
主控列表,以显示每个Windows
函数
可能返回的所有错误代码。可惜,答案是没有这样的列表,而且Microsoft公司将永远不会建立这样的
一个
列表。因为在创建系统的新版本时,建立和维护该列表实在太困难了。 建立这样
一个
列表时存在的问题是,你可以调用
一个
Windows
函数
,但是该
函数
能够在内部调用另
一个
函数
,而这另
一个
函数
又可以调用另
一个
函数
,如此类推。由于各种不同的原因,这些
函数
中的任何
一个
函数
都可能运行失败。有时,当
一个
函数
运行失败时,较高级的
函数
对它进行恢复,并且仍然可以执行你想执行的操作。为了创建该主控列表,Microsoft公司必须跟踪每个
函数
的运行路径,并建立所有可能的错误代码的列表。这项工作很困难。当创建系统的新版本时,这些
函数
的运行路径就会改变。 1.1 你也能够定义自己的错误代码 好了,我已经说明Windows
函数
是如何向
函数
的调用者指明发生的错误。Microsoft公司也使你能够将该机制用于你自己的
函数
。比如说,你编写了
一个
你希望其他人调用的
函数
。你的
函数
可能因为这样或那样的原因而运行失败,你必须向
函数
的调用者说明它已经运行失败。 若要指明
函数
运行失败,你只需要设定线程的最后的错误代码,然后让你的
函数
返回FALSE,INVALID_HANDLE_VALUE,NULL,或者返回任何合适的信息。若要设定线程的最后错误代码,你只需要调用下面的代码: 见原书P8的程序(2) 请将你认为合适的任何32位号码传递给该
函数
。我设法使用WinError.h中已经存在的代码,只要该代码能够正确地指明我想要报告的错误即可。如果你认为WinError.h中的任何代码都不能正确地反映该错误的性质,那么你可以创建你自己的代码。错误代码是个32位的数字,它可以划分成下表所示的各个域。 位 31-30 29 28 27-16 15-0 内容 严重性 Microsoft/ 保留 设备代码 异常代码 客户 含义 0=成功 0=Microsoft 必须是0 由Microsoft 由Microsoft/ 1=供参考 公司定义的 公司定义 客户定义 2=警告 代码 3=错误 1=客户定义 的代码 这些域将在第24章中详细讲述。现在,你需要知道的重要域是第29位的信息。Microsoft公司规定,他们建立的所有错误代码的这个信息位均使用0。如果你创建自己的错误代码,你必须在这个信息位中输入1。这样,就可以确保你的错误代码与Microsoft公司目前或者将来定义的错误代码不会发生冲突, 1.2 ErrorShow示例应用程序 ErrorShow应用程序“01 ErrorShow.exe"(在图1-2中列出)展示了如何获取错误代码的文本描述的方法。该应用程序的源代码和资源文件位于本书所附光盘上的01-ErrorShow目录下。一般来说,该应用程序用于显示调试程序的Watch窗口和Error Lookup程序是如何运行的。当你启动该程序时,就会出现下面这个窗口。 见原书P9的插图 你可以将任何错误代码键入该编辑控件。当你单击Look Up按钮时,在底部的滚动窗口中就会显示该错误的文本描述。该应用程序唯一令人感兴趣的特性是如何调用FormatMessage
函数
。下面是我使用该
函数
的方法: 见原书P10的程序(1) 第
一个
代码行用于从编辑控件中
检索
错误代码的号码。然后,建立
一个
内存块的句柄并将它初始化为NULL。FormatMessage
函数
在内部对内存块进行分配,并将它的句柄返回给我们。 当调用FormatMessage
函数
时,我传递了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage
函数
,我们想要系统定义的错误代码的字符串。我还传递了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,告诉该
函数
为错误代码的文本描述分配足够大的内存块。该内存块的句柄将在hlocal变量中返回。第三个参数指明我们想要查找的错误代码的号码,第四个参数指明我们想要文本描述使用什么语言。 如果FormatMessage
函数
运行成功,那么错误代码的文本描述就位于内存块中,我将它拷贝到对话框底部的滚动窗口中。如果FormatMesage
函数
运行失败,我设法查看NetMsg.dll模块中的消息代码,以了解该错误是否与网络有关。使用NetMsg.dll模块的句柄,我再次调用FormatMessage
函数
。你会看到,每个DLL(或.exe)都有它自己的一组错误代码,你可以使用Message Compiler(MC.exe)将这组错误代码添加给该模块,并将
一个
资源添加给该模块。这就是Visual Studio的Error Lookup工具允许你用Modules对话框进行的操作。 图1-2 ErrorShow示例应用程序 见原书P11—16 第2章 UNICODE 随着Microsoft公司的Windows操作系统在全世界日益广泛的流行,对于我们这些软件开发人员来说,将我们的目标瞄准国际上的各个不同市场,已经成为
一个
越来越重要的问题。美国的软件版本比国际版本提前6个月推向市场,这曾经是个司空见惯的现象。但是,由于各国对Windows操作系统提供了越来越多的支持,因此就更加容易为国际市场生产各种应用软件,从而缩短了软件的美国版本与国际版本推出的时间间隔。 Windows操作系统始终不逾地提供各种支持,以帮助软件开发人员进行应用程序的本地化工作。应用软件可以从各种不同的
函数
中获得特定国家的信息,并可观察控制面板的设置,以确定用户的首选项。Windows甚至支持不同的字体,以适应我们的应用的需要。 我之所以将这一章放在本书的开头,是因为我考虑到Unicode是开发任何应用程序时要采用的基本步骤。关于Unicode的问题,我在本书的每一章中几乎都要讲到,而且本书中给出的所有示例应用程序都是“用Unicode实现的”。如果你为Microsoft Windows 2000或Microsoft Windows CE开发应用程序,你应该使用Unicode进行开发。如果你为Microsoft Windows 98开发应用程序,你必须对某些问题作出决定。本章也要讲述Windows 98的有关问题。 2.1 字符集 软件的本地化要解决的真正问题,实际上就是如何来处理不同的字符集。多年来,我们许多人一直将文本串作为一系列单字节字符来进行编码,并在结尾处放上
一个
零。对于我们来说,这已经成了习惯。当我们调用strlen
函数
时,它在以0结尾的单字节字符数组中返回字符的数目。 问题是,有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了,因此单字节(它提供的符号最多不能超过256个)是根本不敷使用的。为此我们创建了双字节字符集(DBCS),以支持这些文字和书写规则。 2.1.1 单字节与双字节字符集 在双字节字符集中,字符串中的每个字符可以包含
一个
字节,也可以包含两个字节。例如,日文中的汉字,如果第
一个
字符在0x81与0x9F之间,或者在0xE0与0xFC之间,那么你就必须观察下
一个
字节,才能确定字符串中的这个完整的字符。如果要使用双字节字符集,对于程序员来说简直是个很大的难题,因为有些字符只有
一个
字节宽,而有些字符则是两个字节宽。 如果只是调用strlen
函数
,那么你无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。
ANSI
的C运行期库中没有配备相应的
函数
,使你能够对双字节字符集进行操作。但是,Microsoft Visual C++的运行期库却包含许多
函数
,如_mbslen,它可以用来操作多字节(既包括单字节也包括双字节)字符串。 为了帮助你对DBCS字符串进行操作,Windows提供了下面的一组帮助
函数
。
函数
描述 PTSTR CharNext 返回字符串中的下
一个
字符的地址 (PCTSTR pszCurrentChar); PTSTR CharPrev 返回字符串中的上
一个
字符的地址 (PCTSTR pszStart, PCTSTR pszCurrentChar); BOOL IsDBCSLendByte 如果该字节是DBCS字符的第
一个
字节,则返 (BYTE bTestChar); 回TRUE 2.1.2 Unicode:宽字节字符集 Unicode是Apple和Xerox公司于1988年建立的
一个
技术标准。1991年,成立了
一个
集团机构负责Unicode的开发和推广应用。该集团由Apple、Compaq、HP、IBM、Microsoft、Oracle、Silicon Graphics、Sybase、Unisys和Xerox等公司组成。(若要了解该集团的全部成员,请通过网址www.Unicode.org查找。)该集团公司负责维护Unicode标准。Unicode的完整描述可以参阅AddisonWesley出版的《Unicode Standard》一书。(该书可以通过网址www.Unicode.org订购。) Unicode提供了一种简单而又一致的表示字符串的方法。Unicode字符串中的所有字符都是16位的字符(两个字节)。它没有专门的字节来指明下
一个
字节是属于同
一个
字符的组成部分,还是
一个
新字符。这意味着你只需要对指针进行递增或递减,就可以遍历字符串中的各个字符。你不再需要调用CharNext,CharPrev和IsDBCSLeadByte之类的
函数
。 由于Unicode用
一个
16位的值来表示每个字符,因此总共可以得到65000个字符,这样,它就能够对世界各国的书面文字中的所有字符进行编码。这远远超过了单字节字符集的256个字符的数目。 目前,已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了Unicode代码点1。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、
区分
标志和其他许多字符。如果你将所有这些字母和符号加在一起,总计约达35000个不同的代码点,这样,总计的65000个代码点中,大约还有一半可供将来扩充时使用。 这65536个字符可以分成不同的区域。下面这个表显示了一部分这样的区域以及分配给这些区域的字符。 16位代码 字符 16位代码 字符 0000-007F ASCII 0300-036F 通用
区分
标志 0080-00FF 拉丁文1字符 0400-04FF 西里尔字母 0100-017F 欧洲拉丁文 0530-058F 亚美尼亚文 0180-01FF 扩充拉丁文 0590-05FF 西伯莱文 0250-02AF 标准拼音 0600-06FF 阿拉伯文 02B0-02FF 修改型字母 0900-097F 梵文 注1. 代码点是指字符集中的
一个
符号的位置 目前尚未分配的代码点大约还有29000个,不过它们是保留供将来使用的。另外,大约有6000个代码点是保留供你个人使用的。 2. 2 为何应该使用Unicode 当你开发应用程序时,你当然应该考虑利用Unicode的优点。即使现在你不打算对你的应用程序进行本地化,开发时将Unicode放在心上,肯定可以简化将来的代码转换工作。此外,Unicode还具备下列功能: * 可以很容易地在不同语言之间进行数据交换 * 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件 * 提高你的应用程序的运行效率(本章后面还要详细介绍) 2.3 Windows 2000与Unicode Windows 2000是使用Unicode从头进行开发的,用于创建窗口、显示文本、进行字符串操作等的所有核心
函数
都需要Unicode字符串。如果你调用任何
一个
Windows
函数
并给它传递
一个
ANSI
字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果你希望
函数
返回
ANSI
字符串,系统就会首先将Unicode字符串转换成
ANSI
字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然,进行这些字符串的转换需要占用系统的时间和内存开销。 例如,如果你调用CreateWindowEx
函数
,并传递类名字和窗口标题文本的非Unicode字符串,那么CreateWindowEx必须分配内存块(在你的进程的默认堆中),将非Unicode字符串转换成Unicode字符串,并将结果存储在分配到的内存块中,然后调用Unicode版本的CreateWindowEx
函数
。 对于用字符串填入缓存的
函数
来说,系统必须首先将Unicode字符串转换成非Unicode字符串,然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作,因此你的应用程序需要更多的内存,并且运行的速度比较慢。通过从头开始用Unicode来开发应用程序,你就能够使你的应用程序更加有效地运行。 2. 4 Windows 98与Unicode Windows 98不是一种全新的操作系统。它继承了16位Windows操作系统的特性,它不是用来处理Unicode。如果要增加对Unicode的支持,其工作量非常大,因此在该产品的特性列表中没有包括这个支持项目。由于这个原因,Windows 98象它的前任产品一样,几乎都是使用
ANSI
字符串来进行所有的内部操作的。 你仍然可以编写用于处理Unicode字符和字符串的Windows应用程序,不过,使用Windows
函数
要难得多。例如,如果你想要调用CreateWindowEx
函数
并将
ANSI
字符串传递给它,这个调用的速度非常快,不需要从你进程的默认堆栈中分配缓存,也不需要进行字符串转换。但是,如果你想要调用CreateWindowEx
函数
并将Unicode字符串传递给它,你就必须明确分配缓存,并调用
函数
,以便执行从Unicode到
ANSI
字符串的转换操作。然后你可以调用CreateWindowEx,传递
ANSI
字符串。当CreateWindowEx
函数
返回时,你就能释放临时缓存。这比使用Windows 2000上的Unicode要麻烦得多。在本章的后面部分中,我要介绍如何在Windows 98下进行这些转换。 虽然Unicode
函数
的大多数代码在Windows 98中不起任何作用,但是有少数Unicode
函数
确实拥有非常有用的实现代码。这些
函数
是: 见原书的P21 可惜的是,这些
函数
中有许多
函数
在Windows 98中会出现各种各样的错误。有些
函数
无法使用某些字体,有些
函数
会破坏内存堆栈,有些
函数
会使打印机驱动程序崩溃,如此等等。如果你要使用这些
函数
,你必须对它们进行大量的测试。即使这样,你可能仍然无法解决问题。因此你必须向用户说明这些情况。 2. 5 Windows CE与Unicode Windows CE操作系统是为小型设备开发的,这些设备的内存很小,并且不带磁盘存储器。你可能认为,由于Microsoft公司的主要目标是建立一种尽可能小的操作系统,因此它会使用
ANSI
作为自己的字符集。但是Microsoft公司并不是鼠目寸光。他们懂得,采用Windows CE的设备要在世界各地销售,他们希望降低软件开发成本,这样就能更加容易地开发应用程序。为此,Windows CE本身就是使用Unicode的一种操作系统。 但是,为了使Windows CE尽量做得小一些,Microsoft公司决定完全不支持
ANSI
Windows
函数
。因此,如果你要为Windows CE开发应用程序,你必须懂得Unicode,并且在整个应用程序中使用Unicode。 2. 6 需要注意的问题 下面让我们进一步明确一下“Microsoft公司对Unicode支持的情况”: * Windows 2000既支持Unicode,也支持
ANSI
,因此你可以为它们当中的任何一种开发应用程序 * Windows 98 只支持
ANSI
,你只能为
ANSI
开发应用程序 * Windows CE只支持Unicode,你只能为Unicode开发应用程序 虽然Microsoft公司试图让软件开发人员能够非常容易地开发在这3种平台上运行是软件,但是Unicode与
ANSI
之间的差异使得事情变得困难起来,并且这种差异通常是我遇到的最大的问题之一。请不要误解,Microsoft公司坚定地支持Unicode,并且我也坚决鼓励你使用它。不过你应该懂得,你可能遇到一些问题,需要一定的时间来解决这些问题。我建议你尽可能使用Unicode。如果你运行Windows 98,那么只有在必要时才要转换到
ANSI
。 不过,还有另
一个
小问题你应该了解,那就是COM。 2.7 对COM的简单说明 当Microsoft公司将COM从16位Windows转换成Win32时,公司作出了
一个
决定,即,需要字符串的所有COM接口方法都只能接受Unicode字符串。这是个了不起的决定,因为COM通常用于使不同的组件能够互相之间进行通信,而Unicode则是传递字符串的最佳手段。 如果你为Windows 2000或Windows CE开发应用程序,并且也使用COM,那么你将会如虎添翼。在你的整个源代码中使用Unicode,将使与操作系统进行通信和与COM对象进行通信的操作变成一件轻而易举的事情。 如果你为Windows 98开发应用程序,并且也使用COM,那么你将会遇到一些问题。COM要求你使用Unicode字符串。操作系统的大多数
函数
要求你使用
ANSI
字符串。那是多么难办的事情啊!我曾经从事过若干个项目的开发,在这些项目中,我编写了许多代码,仅仅是为了来回进行字符串的转换。 2. 8 如何编写Unicode源代码 Microsoft公司为Unicode设计了Windows API,这样,它可以尽量减少对你的代码的影响。实际上,你可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。你只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。 2. 8.1 C运行期库对Unicode的支持 为了利用Unicode字符串,因此定义了一些数据类型。标准的C头文件String.h已经作了修改,以便定义
一个
名字为wchar_t的数据类型,它是
一个
Unicode字符的数据类型: 见原书P23的程序(1) 例如,如果你想要创建
一个
缓存,用于存放最多为99个字符的Unicode字符串和
一个
结尾为零的字符,你可以使用下面这个语句: 见原书P23的程序(2) 该语句创建了
一个
由100个16位值组成的数组。当然,标准的C运行期字符串
函数
,如strcpy、strchr和strcat等,只能对
ANSI
字符串进行操作,它们不能正确地处理Unicode字符串。因此,
ANSI
C也拥有一组补充
函数
。图2-1显示了一些标准的
ANSI
C字符串
函数
,后面是它们的等价Unicode
函数
。 图2-1 标准的
ANSI
C字符串
函数
和它们的等价Unicode
函数
见原书P23的程序(3)和P24的程序 请注意,所有的Unicode
函数
均以wcs开头,wcs是宽字符串的英文缩写。若要调用Unicode
函数
,只需用前缀wcs来取代任何
ANSI
字符串
函数
的前缀str即可。 说明 大多数软件开发人员可能已经不记得这样
一个
非常重要的问题了,那就 是Microsoft公司提供的C运行期库与
ANSI
的标准C运行期库是一致的。
ANSI
C规定,C运行期库支持Unicode字符和字符串。这意味着你始终都可 以调用C运行期
函数
,以便对Unicode字符和字符串进行操作,即使你是在 Windows 98上运行,也可以调用这些
函数
。换句话说,wcscat,wcslen和wcstok 等
函数
都能够在Windows 98上很好地运行,这些都是你必须关心的操作系统
函数
。 对于包含了对str
函数
或wcs
函数
进行显式调用的代码来说,你无法非常容易地同时为
ANSI
和Unicode对这些代码进行编译。在本章前面部分的内容中,我说过可以创建同时为
ANSI
和Unicode进行编译的单个源代码文件。若要建立这种双重功能,你必须包含Tchar.h文件,而不是包含String.h文件。 Tchar.h文件的唯一作用是帮助你创建
ANSI
/Unicode通用源代码文件。它包含你应该用在源代码中的一组宏,而不应该直接调用str
函数
或者wcs
函数
。如果你在编译源代码文件时定义了_UNICODE,这些宏就会引用wcs这组
函数
。如果你没有定义_UNICODE,那么这些宏将引用str这组宏。 例如,在Tchar.h中有
一个
宏称为_tcscpy。如果在你包含该头文件时没有定义_UNICODE,那么_tcscpy就会扩展为
ANSI
的strcpy
函数
。但是如果定义了_UNICODE,_tcscpy将扩展为Unicode的wcscpy
函数
。拥有字符串参数的所有C运行期
函数
都在Tchar.h文件中定义了
一个
通用宏。如果你使用通用宏,而不是
ANSI
/Unicode的特定
函数
名,你就能够顺利地创建可以为
ANSI
或Unicode进行编译的源代码。 但是,除了使用这些宏之外,还有一些操作你是必须进行的。Tchar.h文件包含了一些其他的宏。 若要定义
一个
ANSI
/Unicode通用的字符串数组,请使用下面的TCHAR数据类型。如果定义了_UNICODE,TCHAR将声明为下面的形式: 见原书P25的程序(1) 如果没有定义_UNICODE,则TCHAR将声明为下面的形式: 见原书P25的程序(2) 使用该数据类型,你可以象下面这样分配
一个
字符串: 见原书P25的程序(3) 你也可以创建对字符串的指针: 见原书P25的程序(4) 不过上面这行代码存在
一个
问题。按照默认设置,Microsoft公司的C++编译器能够编译所有的字符串,就象它们是
ANSI
字符串,而不是Unicode字符串。因此,如果没有定义_UNICODE,该编译器将能正确地编译这一行代码。但是,如果定义了_UNICODE,就会产生
一个
错误。若要生成
一个
Unicode字符串而不是
ANSI
字符串,你必须将该代码行改写为下面的样子: 见原书P25的程序(5) 原义字符串前面的大写字母L,用于告诉编译器该字符串应该作为Unicode字符串来编译。当编译器将字符串置于程序的数据部分中时,它在每个字符之间分散插入零字节。这种变更带来的问题是,现在只有当定义了_UNICODE时,程序才能成功地进行编译。我们需要另
一个
宏,以便有选择地在原义字符串的前面加上大写字母L。这项工作由_TEXT宏来完成,_TEXT宏也在Tchar.h文件中做了定义。如果定义了_UNICODE,那么_TEXT定义为下面的形式: 见原书P25的程序(6)
数据库技术及应用:第4章关系数据库语言SQL.ppt
数据库技术及应用:第4章关系数据库语言SQL.ppt
关系数据库语言SQL知识介绍.pptx
关系数据库语言SQL知识介绍.pptx
英特尔边缘计算技术
568
社区成员
7,024
社区内容
发帖
与我相关
我的任务
英特尔边缘计算技术
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
复制链接
扫一扫
分享
社区描述
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章