下载
加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 VC++_CString的操作

VC++_CString的操作.doc

VC++_CString的操作

junzifei
2013-03-20 0人阅读 举报 0 0 暂无简介

简介:本文档为《VC++_CString的操作doc》,可适用于IT/计算机领域

vc中的CString的操作原著:JosephMNewcomer翻译:littleloach原文出处:codeproject:CStringManagement通过阅读本文你能学习怎么有效地使用CString。  CString是一种非常有用的数据类型。他们非常大程度上简化了MFC中的许多操作使得MFC在做字符串操作的时候方便了非常多。不管怎样使用CString有非常多特别的技巧特别是对于纯C背景下走出来的程式员来说有点难以学习。这篇文章就来讨论这些技巧。  使用CString能让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册但囊括了大部分常见基本问题。这篇文章包括以下内容:CString对象的连接格式化字符串(包括int型转化为CString)CString型转化成int型CString型和char*类型的相互转化char*转化成CStringCString转化成char*之一:使用LPCTSTR强制转化CString转化成char*之二:使用CString对象的GetBuffer方法CString转化成char*之三:和控件的接口CString型转化成BSTR型BSTR型转化成CString型VARIANT型转化成CString型载入字符串表资源CString和临时对象CString的效率总结下面我分别讨论。CStringgray("Gray")CStringcat("Cat")CStringgraycat=graycat要比用下面的方法好得多:chargray="Gray"charcat="Cat"char*graycat=malloc(strlen(gray)strlen(cat))strcpy(graycat,gray)strcat(graycat,cat)、格式化字符串  和其用sprintf()函数或wsprintf()函数来格式化一个字符串还不如用CString对象的Format()方法:CStringssFormat(T("Thetotalisd"),total)  用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大这些工作由CString类替你完成。  格式化是一种把其他不是字符串类型的数据转化为CString类型的最常用技巧比如把一个整数转化成CString类型可用如下方法:CStringssFormat(T("d"),total)  我总是对我的字符串使用T()宏这是为了让我的代码至少有Unicode的意识当然关于Unicode的话题不在这篇文章的讨论范围。T()宏在位字符环境下是如下定义的:#defineT(x)x非Unicode版本(nonUnicodeversion)而在Unicode环境下是如下定义的:#defineT(x)L##xUnicode版本(Unicodeversion)所以在Unicode环境下他的效果就相当于:sFormat(L"d",total)  如果你认为你的程式可能在Unicode的环境下运行那么开始在意用Unicode编码。比如说不要用sizeof()操作符来获得字符串的长度因为在Unicode环境下就会有倍的误差。我们能用一些方法来隐藏Unicode的一些细节比如在我需要获得字符长度的时候我会用一个叫做DIM的宏这个宏是在我的dimh文件中定义的我会在我写的所有程式中都包含这个文件:#defineDIM(x)(sizeof((x))sizeof((x)))  这个宏不仅能用来解决Unicode的字符串长度的问题也能用在编译时定义的表格上他能获得表格的项数如下:classWhatever{}Whateverdata={{},{},}for(inti=i<DIM(data)i)扫描表格寻找匹配项。  这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用如果你传递字符个数给他他将不能正常工作。如下:TCHARdatalstrcpyn(data,longstring,sizeof(data))WRONG!lstrcpyn(data,longstring,DIM(data))RIGHTWriteFile(f,data,DIM(data),bytesWritten,)WRONG!WriteFile(f,data,sizeof(data),bytesWritten,)RIGHT造成以上原因是因为lstrcpyn需要一个字符个数作为参数不过WriteFile却需要字节数作为参数。同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度你可能会认为你应该这样做:WriteFile(f,data,lstrlen(data),bytesWritten,)WRONG不过在Unicode环境下他不会正常工作。正确的做法应该是这样:WriteFile(f,data,lstrlen(data)*sizeof(TCHAR),bytesWritten,)RIGHT  因为WriteFile需要的是个以字节为单位的长度。(可能有些人会想“在非Unicode的环境下运行这行代码就意味着总是在做一个多余的乘操作这样不会降低程式的效率吗?”这种想法是多余的你必须要了解编译器实际上做了什么没有哪一个C或C编译器会把这种无聊的乘操作留在代码中。在Unicode环境下运行的时候你也不必担心那个乘操作会降低程式的效率记住这只是个左移一位的操作而已编译器也非常乐意为你做这种替换。)  使用T宏并不是意味着你已创建了一个Unicode的程式你只是创建了一个有Unicode意识的程式而已。如果你在默认的bit模式下编译你的程式的话得到的将是个普通的bit的应用程式(这里的bit指的只是位的字符编码并不是指位的计算机系统)当你在Unicode环境下编译你的程式时你才会得到一个Unicode的程式。记住CString在Unicode环境下里面包含的可都是位的字符哦。、CString型转化成int型  把CString类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。  虽然通常你怀疑使用atoi()函数是个好的选择他也非常少会是个正确的选择。如果你准备使用Unicode字符你应该用ttoi()他在ANSI编码系统中被编译成atoi()而在Unicode编码系统中编译成wtoi()。你也能考虑使用tcstoul()或tcstol()他们都能把字符串转化成任意进制的长整数(如二进制、八进制、十进制或十六进制)不同点在于前者转化后的数据是无符号的(unsigned)而后者相反。看下面的例子:CStringhex=T("FAB")CStringdecimal=T("")ASSERT(tcstoul(hex,,)==ttoi(decimal))、CString型和char*类型的相互转化  这是初学者使用CString时最常见的问题。有了C的帮助非常多问题你不必深入的去考虑他直接拿来用就行了不过如果你不能深入了解他的运行机制又会有非常多问题让你迷惑特别是有些看起来没有问题的代码却偏偏不能正常工作。比如你会奇怪为什么不能写向下面这样的代码呢:CStringgraycat="Gray""Cat"或这样:CStringgraycat("Gray""Cat")  事实上编译器将抱怨上面的这些尝试。为什么呢?因为针对CString和LPCTSTR数据类型的各种各样的组合“”运算符被定义成一个重载操作符。而不是两个LPCTSTR数据类型他是底层数据类型。你不能对基本数据(如int、char或char*)类型重载C的运算符。你能象下面这样做:CStringgraycat=CString("Gray")CString("Cat")或这样:CStringgraycat=CString("Gray")"Cat"研究一番就会发现:“”总是使用在至少有一个CString对象和一个LPCSTR的场合。注意编写有Unicode意识的代码总是一件好事比如:CStringgraycat=CString(T("Gray"))T("Cat")这将使得你的代码能直接移植。char*转化为CString  目前你有一个char*类型的数据或说一个字符串。怎么样创建CString对象呢?这里有一些例子:char*p="Thisisatest"或象下面这样更具有Unicode意识:TCHAR*p=T("Thisisatest")或LPTSTRp=T("Thisisatest")你能使用下面任意一种写法:CStrings="Thisisatest"bitonlyCStrings=T("Thisisatest")UnicodeawareCStrings("Thisisatest")bitonlyCStrings(T("Thisisatest"))UnicodeawareCStrings=pCStrings(p)  用这些方法能轻松将常量字符串或指针转换成CString。需要注意的是字符的赋值总是被拷贝到CString对象中去的所以你能象下面这样操作:TCHAR*p=T("Gray")CStrings(p)p=T("Cat")s=p结果字符串肯定是“GrayCat”。CString类更有几个其他的构造函数不过这里我们不考虑他如果你有兴趣能自己查看相关文件。事实上CString类的构造函数比我展示的要复杂比如:CStrings="Thisisatest"  这是非常草率的编码不过实际上他在Unicode环境下能编译通过。他在运行时调用构造函数的MultiByteToWideChar操作将位字符串转换成位字符串。不管怎样如果char*指针是网络上传输的位数据这种转换是非常有用的。CString转化成char*之一:强制类型转换为LPCTSTR  这是一种略微硬性的转换有关“正确”的做法人们在认识上还存在许多混乱正确的使用方法有非常多但错误的使用方法可能和正确的使用方法相同多。  我们首先要了解CString是一种非常特别的C对象他里面包含了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数及一个缓冲区长度。有效字符数的大小能是从到该缓冲最大长度值减之间的所有数(因为字符串结尾有一个字符)。字符记数和缓冲区长度被巧妙隐藏。  除非你做一些特别的操作否则你不可能知道给CString对象分配的缓冲区的长度。这样即使你获得了该缓冲的地址你也无法更改其中的内容不能截短字符串也绝对没有办法加长他的内容否则第一时间就会看到溢出。  LPCTSTR操作符(或更明确地说就是TCHAR*操作符)在CString类中被重载了该操作符的定义是返回缓冲区的地址因此如果你需要一个指向CString的字符串指针的话能这样做:CStrings("GrayCat")LPCTSTRp=s  他能正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时C规测容许这种选择。比如你能将(浮点数)定义为将某个复数(有一对浮点数)进行强制类型转换后只返回该复数的第一个浮点数(也就是其实部)。能象下面这样:Complexc(f,f)floatrealpart=c如果(float)操作符定义正确的话那么实部的的值应该是。  这种强制转化适合所有这种情况例如所有带有LPCTSTR类型参数的函数都会强制执行这种转换。于是你可能有这样一个函数(也许在某个你买来的DLL中):BOOLDoSomethingCool(LPCTSTRs)你象下面这样调用他:CStringfile("c:\\myfiles\\coolstuff")BOOLresult=DoSomethingCool(file)  他能正确运行。因为DoSomethingCool函数已说明了需要一个LPCTSTR类型的参数因此LPCTSTR被应用于该参数在MFC中就是返回的串地址。如果你要格式化字符串怎么办呢?CStringgraycat("GrayCat")CStringssFormat("Mew!Iloves",graycat)  注意由于在可变参数列表中的值(在函数说明中是以“”表示的)并没有隐含一个强制类型转换操作符。你会得到什么结果呢?  一个令人惊讶的结果我们得到的实际结果串是:"Mew!IloveGrayCat"。  因为MFC的设计者们在设计CString数据类型时非常小心CString类型表达式求值后指向了字符串所以这里看不到所有象Format或sprintf中的强制类型转换你仍然能得到正确的行为。描述CString的附加数据实际上在CString名义地址之后。  有一件事情你是不能做的那就是修改字符串。比如你可能会尝试用“,”代替“”(不要做这样的如果你在乎国际化问题你应该使用十进制转换的NationalLanguageSupport特性)下面是个简单的例子:CStringv("")货币金额两位小数LPCTSTRp=vplstrlen(p)=’’,’’  这时编译器会报错因为你赋值了一个常量串。如果你做如下尝试编译器也会错:strcat(p,"each")  因为strcat的第一个参数应该是LPTSTR类型的数据而你却给了一个LPCTSTR。  不要试图钻这个错误消息的牛角尖这只会使你自己陷入麻烦!  原因是缓冲有一个计数他是不可存取的(他位于CString地址之下的一个隐藏区域)如果你改动这个串缓冲中的字符计数不会反映所做的修改。此外如果字符串长度恰好是该字符串物理限制的长度(梢后还会讲到这个问题)那么扩展该字符串将改写缓冲以外的所有数据那是你无权进行写操作的内存(不对吗?)你会毁换坏不属于你的内存。这是应用程式真正的死亡处方。CString转化成char*之二:使用CString对象的GetBuffer方法  如果你需要修改CString中的内容他有一个特别的方法能使用那就是GetBuffer他的作用是返回一个可写的缓冲指针。如果你只是打算修改字符或截短字符串你完万能这样做:CStrings(T("Fileext"))LPTSTRp=sGetBuffer()LPTSTRdot=strchr(p,’’’’)OK,shouldhaveusedsFindif(p!=)*p=T(’’\’’)sReleaseBuffer()  这是GetBuffer的第一种用法也是最简单的一种不用给他传递参数他使用默认值意思是:“给我这个字符串的指针我确保不加长他”。当你调用ReleaseBuffer时字符串的实际长度会被重新计算然后存入CString对象中。  必须强调一点在GetBuffer和ReleaseBuffer之间这个范围一定不能使用你要操作的这个缓冲的CString对象的所有方法。因为ReleaseBuffer被调用之前该CString对象的完整性得不到保障。研究以下代码:CStrings()LPTSTRp=sGetBuffer()这个指针p发生了非常多事情intn=sGetLength()非常糟D!!!!!有可能给出错误的答案!!!sTrimRight()非常糟!!!!!不能确保能正常工作!!!!sReleaseBuffer()目前应该OKintm=sGetLength()这个结果能确保是正确的。sTrimRight()将正常工作。  假设你想增加字符串的长度你首先要知道这个字符串可能会有多长好比是声明字符串数组的时候用:charbuffer表示个字符空间足以让你做所有想做得事情。在CString中和之意义相等的表示法:LPTSTRp=sGetBuffer()  调用这个函数后你不仅获得了字符串缓冲区的指针而且同时还获得了长度至少为个字符的空间(注意我说的是“字符”而不是“字节”因为CString是以隐含方式感知Unicode的)。  同时还应该注意的是如果你有一个常量串指针这个串本身的值被存储在只读内存中如果试图存储他即使你已调用了GetBuffer并获得一个只读内存的指针存入操作会失败并报告存取错误。我没有在CString上证实这一点但我看到过大把的C程式员经常犯这个错误。  C程式员有一个通病是分配一个固定长度的缓冲对他进行sprintf操作然后将他赋值给一个CString:charbuffersprintf(buffer,"",args,)部分省略许多细节CStrings=buffer虽然更好的形式能这么做:CStringssFormat(T(""),args,)如果你的字符串长度万一超过个字符的时候不会破坏堆栈。  另外一个常见的错误是:既然固定大小的内存不工作那么就采用动态分配字节这种做法弊端更大:intlen=lstrlen(parm)lstrlen(parm)char*buffer=newcharlensprintf(buffer,"sisequaltos,validdata",parm,parm)CStrings=bufferdeletebuffer他能能被简单地写成:CStringssFormat(T("sisequaltos,validdata"),parm,parm)  需要注意sprintf例子都不是Unicode就绪的尽管你能使用tsprintf及用T()来包围格式化字符串不过基本思路仍然是在走弯路这这样非常容易出错。CStringtochar*之三:和控件的接口  我们经常需要把一个CString的值传递给一个控件比如CTreeCtrl。MFC为我们提供了非常多便利来重载这个操作不过在大多数情况下你使用“原始”形式的更新因此需要将墨某个串指针存储到TVINSERTITEMSTRUCT结构的TVITEM成员中。如下:TVINSERTITEMSTRUCTtviCStrings为s赋一些值。tviitempszText=sCompileryellsatyouhere填写tvi的其他域HTREEITEMti=cMyTreeInsertItem(tvi)  为什么编译器会报错呢?明明看起来非常完美的用法啊!不过事实上如果你看看TVITEM结构的定义你就会明白在TVITEM结构中pszText 成员的声明如下:LPTSTRpszTextintcchTextMax  因此赋值不是赋给一个LPCTSTR类型的变量而且编译器无法知道怎么将赋值语句右边强制转换成LPCTSTR。好吧你说那我就改成这样:tviitempszText=(LPCTSTR)s编译器依然会报错。  编译器之所以依然报错是因为你试图把一个LPCTSTR类型的变量赋值给一个LPTSTR类型的变量这种操作在C或C中是被禁止的。你不能用这种方法来滥用常量指针和非常量指针概念否则会扰乱编译器的优化机制使之不知怎么优化你的程式。比如如果你这么做:constinti=dolotsofstuff=aiusagelotsmorestuff=aiusage  那么编译器会以为既然i是const所以usage和usage的值是相同的并且他甚至能事先计算好usage处的ai的地址然后保留着在后面的usage处使用而不是重新计算。如果你按如下方式写的话:constinti=int*p=idolotsofstuff=aiusagelotsmorestuff(*p)messovercompiler’’sassumptionandotherstuff=aiusage  编译器将认为i是常量从而ai的位置也是常量这样间接地破坏了先前的假设。因此你的程式将会在debug编译模式(没有优化)和release编译模式(完全优化)中反映出不同的行为这种情况可不好所以当你试图把指向i的指针赋值给一个可修改的引用时会被编译器诊断为这是一种伪造。这就是为什么(LPCTSTR)强制类型转化不起作用的原因。  为什么不把该成员声明成LPCTSTR类型呢?因为这个结构被用于读写控件。当你向控件写数据时文本指针实际上被当成LPCTSTR而当你从控件读数据时你必须有一个可写的字符串。这个结构无法区分他是用来读还是用来写。因此你会常常在我的代码中看到如下的用法:tviitempszText=(LPTSTR)(LPCTSTR)s  他把CString强制类型转化成LPCTSTR也就是说先获得改字符串的地址然后再强制类型转化成LPTSTR以便能对之进行赋值操作。注意这只有在使用Set或Insert之类的方法才有效!如果你试图获取数据则不能这么做。  如果你打算获取存储在控件中的数据则方法稍有不同例如对某个CTreeCtrl使用GetItem方法我想获取项目的文本。我知道这些文本的长度不会超过MYLIMIT因此我能这样写:TVITEMtviassortedinitializationofotherfieldsoftvitvipszText=sGetBuffer(MYLIMIT)tvicchTextMax=MYLIMITcMyTreeGetItem(tvi)sReleaseBuffer()  能看出来其实上面的代码对所有类型的Set方法都适用不过并不必这么做因为所有的类Set方法(包括Insert方法)不会改动字符串的内容。不过当你需要写CString对象时必须确保缓冲是可写的这正是GetBuffer所做的事情。再次强调:一旦做了一次GetBuffer调用那么在调用ReleaseBuffer之前不要对这个CString对象做所有操作。、CString型转化成BSTR型  当我们使用ActiveX控件编程时经常需要用到将某个值表示成BSTR类型。BSTR是一种记数字符串Intel平台上的宽字符串(Unicode)并且能包含嵌入的字符。你能调用CString对象的AllocSysString方法将CString转化成BSTR:CStringss=whateverBSTRb=sAllocSysString()  目前指针b指向的就是个新分配的BSTR对象该对象是CString的一个拷贝包含终结字符。目前你能将他传递给所有需要BSTR的接口。通常BSTR由接收他的组件来释放如果你需要自己释放BSTR的话能这么做:::SysFreeString(b)  对于怎么表示传递给ActiveX控件的字符串在微软内部曾一度争论不休最后VisualBasic的人占了上风BSTR(“BasicString”的首字母缩写)就是这场争论的结果。、BSTR型转化成CString型  由于BSTR是记数Unicode字符串你能用标准转换方法来创建位的CString。实际上这是CString内建的功能。在CString中有特别的构造函数能把ANSI转化成Unicode也能把Unicode转化成ANSI。你同样能从VARIANT类型的变量中获得BSTR类型的字符串VARIANT类型是由各种COM和Automation(自动化)调用返回的类型。例如在一个ANSI程式中:BSTRbb=whateverCStrings(b==L"":b)  对于单个的BSTR串来说这种用法能工作得非常好这是因为CString有一个特别的构造函数以LPCWSTR(BSTR正是这种类型)为参数并将他转化成ANSI类型。专门检查是必须的因为BSTR可能为空值而CString的构造函数对于值情况考虑的不是非常周到(感谢BrianRoss指出这一点!)。这种用法也只能处理包含NUL终结字符的单字符串如果要转化含有多个字符串你得额外做一些工作才行。在CString中内嵌的字符通常表现不尽如人意应该尽量避免。  根据CC规则如果你有一个LPWSTR那么他别无选择只能和LPCWSTR参数匹配。在Unicode模式下他的构造函数是:CString::CString(LPCTSTR)正如上面所表示的在ANSI模式下他有一个特别的构造函数:CString::CString(LPCWSTR)  他会调用一个内部的函数将Unicode字符串转换成ANSI字符串。(在Unicode模式下有一个专门的构造函数该函数有一个参数是LPCSTR类型一个位ANSI字符串指针该函数将他加宽为Unicode的字符串!)再次强调:一定要检查BSTR的值是否为。  另外更有一个问题正如上文提到的:BSTRs能含有多个内嵌的字符不过CString的构造函数只能处理某个串中单个字符。也就是说如果串中含有嵌入的NUL字节CString将会计算出错误的串长度。你必须自己处理他。如果你看看strcorecpp中的构造函数你会发现他们都调用了lstrlen也就是计算字符串的长度。  注意从Unicode到ANSI的转换使用带专门参数的::WideCharToMultiByte如果你不想使用这种默认的转换方式则必须编写自己的转化代码。  如果你在UNICODE模式下编译代码你能简单地写成:CStringconvert(BSTRb){if(b==)returnCString(T(""))CStrings(b)inUNICODEmodereturns}  如果是ANSI模式则需要更复杂的过程来转换。注意这个代码使用和::WideCharToMultiByte相同的参数值。所以你只能在想要改动这些参数进行转换时使用该技术。例如指定不同的默认字符不同的标志集等。CStringconvert(BSTRb){CStringsif(b==)returnsemptyforBSTR#ifdefUNICODEs=b#elseLPSTRp=sGetBuffer(SysStringLen(b))::WideCharToMultiByte(CPACP,ANSICodePage,noflagsb,sourcewidecharstring,assumeNULterminatedp,targetbufferSysStringLen(b),targetbufferlength,usesystemdefaultchar)don’’tcareifdefaultusedsReleaseBuffer()#endifreturns}  我并不担心如果BSTR包含没有映射到位字符集的Unicode字符时会发生什么因为我指定了::WideCharToMultiByte的最后两个参数为。这就是你可能需要改动的地方。、VARIANT型转化成CString型  事实上我从来没有这么做过因为我没有用COMOLEActiveX编写过程式。不过我在microsoftpublicvcmfc新闻组上看到了RobertQuirk的一篇帖子谈到了这种转化我觉得把他的文章包含在我的文章里是不太好的做法所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。  VARIANT类型经常用来给COM对象传递参数或接收从COM对象返回的值。你也能自己编写返回VARIANT类型的方法函数返回什么类型依赖可能(并且常常)方法的输入参数(比如在自动化操作中依赖和你调用哪个方法。IDispatch::Invoke可能返回(通过其一个参数)一个包含有BYTE、WORD、float、double、date、BSTR等等VARIANT类型的结果(详见MSDN上的VARIANT结构的定义)。在下面的例子中假设类型是个BSTR的变体也就是说在串中的值是通过bsrtVal来引用其好处是在ANSI应用中有一个构造函数会把LPCWCHAR引用的值转换为一个CString(见BSTRtoCString部分)。在Unicode模式中将成为标准的CString构造函数参见对缺省::WideCharToMultiByte转换的告诫及你觉得是否能接受(大多数情况下你会满意的)。VARIANTvaDatavaData=mcomYourMethodHere()ASSERT(vaDatavt==VTBSTR)CStringstrData(vaDatabstrVal)你还能根据vt域的不同来建立更通用的转换例程。为此你可能会考虑:CStringVariantToString(VARIANT*va){CStringsswitch(va>vt){*vt*caseVTBSTR:returnCString(vaData>bstrVal)caseVTBSTR|VTBYREF:returnCString(*vaData>pbstrVal)caseVTI:sFormat(T("d"),va>lVal)returnscaseVTI|VTBYREF:sFormat(T("d"),*va>plVal)caseVTR:sFormat(T("f"),va>dblVal)returns剩下的类型转换由读者自己完成default:ASSERT(FALSE)unknownVARIANTtype(thisASSERTisoptional)returnCString("")}*vt*}、载入字符串表资源  如果你想创建一个容易进行语言版本移植的应用程式你就不能在你的原始码中直接包含本土语言字符串(下面这些例子我用的语言都是英语因为我的本土语是英语)比如下面这种写法就非常糟:CStrings="Thereisanerror"  你应该把你所有特定语言的字符串独立摆放(调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:sFormat(T("ds"),code,text)  在你的程式中文字字符串不是语言敏感的。不管怎样你必须非常小心不要使用下面这样的串:fmtis"Errorinsfiles"readorwriteis"reading"or"writing"sFormat(fmt,readorwrite,filename)  这是我的切身体会。在我的第一个国际化的应用程式中我犯了这个错误尽管我懂德语知道在德语的语法中动词放在句子的最后面我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让他们能正常工作。比较好的办法(也是我目前使用的办法)是使用两个字符串一个用于读一个用于写在使用时加载合适的版本使得他们对字符串参数是非敏感的。也就是说加载整个格式而不是加载串“reading”“writing”:fmtis"Errorinreadingfiles""Errorinwritingfiles"sFormat(fmt,filename)  一定要注意如果你有好几个地方需要替换你一定要确保替换后句子的结构不会出现问题比如在英语中能是主语宾语主语谓语动词宾语的结构等等。  在这里我们并不讨论FormatMessage其实他比sprintfFormat还要有优势不过不太容易和CString结合使用。解决这种问题的办法就是我们按照参数出目前参数表中的位置给参数取名字这样在你输出的时候就不会把他们的位置排错了。  接下来我们讨论我们这些独立的字符串放在什么地方。我们能把字符串的值放入资源文件中的一个称为STRINGTABLE的段中。过程如下:首先使用VisualStudio的资源编辑器创建一个字符串然后给每一个字符串取一个ID一般我们给他取名字都以IDS开头。所以如果你有一个信息你能创建一个字符串资源然后取名为IDSREADINGFILE另外一个就取名为IDSWRITINGFILE。他们以下面的形式出目前你的rc文件中:STRINGTABLEIDSREADINGFILE"Readingfiles"IDSWRITINGFILE"Writingfiles"END注意:这些资源都以Unicode的格式保存不管你是在什么环境下编译。他们在Winx系统上也是以Unicode的形式存在虽然Winx不能真正处理Unicode。然后你能这样使用这些资源:在使用资源串表之前程式是这样写的:CStringfmtif()fmt="Readingfiles"elsefmt="Writingfiles"muchlaterCStringssFormat(fmt,filename)使用资源串表之后程式这样写:CStringfmtif()fmtLoadString(IDSREADINGFILE)elsefmtLoadString(DSWRITINGFILE)muchlaterCStringssFormat(fmt,filename)  目前你的代码能移植到所有语言中去。LoadString方法需要一个字符串资源的ID作为参数然后他从STRINGTABLE中取出他对应的字符串赋值给CString对象。CString对象的构造函数更有一个更加聪明的特征能简化STRINGTABLE的使用。这个用法在CString::CString的文件中没有指出不过在构造函数的示例程式中使用了。(为什么这个特性没有成为正式文件的一部分而是放在了一个例子中我记不得了!)【译者注:从这句话看作者可能是CString的设计者。其实前面更有一句类似的话。说他没有对使用GetBuffer()获得的指针指向的地址是否可读做有效性检查】。这个特征就是:如果你将一个字符串资源的ID强制类型转换为LPCTSTR将会隐含调用LoadString。因此下面两个构造字符串的例子具有相同的效果而且其ASSERT在debug模式下不会被触发:CStringssLoadString(IDSWHATEVER)CStringt((LPCTSTR)IDSWHATEVER)ASSERT(s==t)不会被触发说明s和t是相同的。  目前你可能会想:这怎么可能工作呢?我们怎么能把STRINGTABLEID转化成一个指针呢?非常简单:所有的字符串ID都在~这个范围内也就是说他所有的高位都是而我们在程式中所使用的指针是不可能小于的因为程式的低K内存永远也不可能存在的如果你试图访问x到xFFFF之间的内存将会引发一个内存越界错误。所以说~的值不可能是个内存地址所以我们能用这些值来作为字符串资源的ID。  我倾向于使用MAKEINTRESOURCE宏显式地做这种转换。我认为这样能让代码更加易于阅读。这是个只适合在MFC中使用的标准宏。你要记住大多数的方法即能接受一个UINT型的参数也能接受一个LPCTSTR型的参数这是依赖C的重载功能做到的。C重载函数带来的弊端就是造成所有的强制类型转化都需要显示声明。同样你也能给非常多种结构只传递一个资源名。CStringssLoadString(IDSWHATEVER)CStringt(MAKEINTRESOURCE(IDSWHATEVER))ASSERT(s==t)  告诉你吧:我不仅只是在这里鼓吹事实上我也是这么做的。在我的代码中你几乎不可能找到一个字符串当然那些只是偶然在调试中出现的或和语言无关的字符串除外。、CString和临时对象  这是出目前microsoftpublicvcmfc新闻组中的一个小问题我简单的提一下这个问题是有个程式员需要往注册表中写入一个字符串他写道:  我试着用RegSetValueEx()设置一个注册表键的值不过他的结果总是令我困惑。当我用char声明一个变量时他能正常工作不过当我用CString的时候总是得到一些垃圾:"YacuteYacuteYacuteYacuteYacuteYacuteYacuteYacuteYacuteYacute"为了确认是不是我的CString数据出了问题我试着用GetBuffer然后强制转化成char*LPCSTR。GetBuffer返回的值是正确的不过当我把他赋值给char*时他就变成垃圾了。以下是我的程式段:char*szName=GetName()GetBuffer()RegSetValueEx(hKey,"Name",,REGSZ,(CONSTBYTE*)szName,strlen(szName))这个Name字符串的长度小于所以我不认为是GetBuffer的参数的问题。真让人困惑请帮帮我。亲爱的Frustrated你犯了一个相当微妙的错误聪明反被聪明误正确的代码应该象下面这样:CStringName=GetName()RegSetValueEx(hKey,T("Name"),,REGSZ,(CONSTBYTE*)(LPCTSTR)Name,(NameGetLength())*sizeof(TCHAR))  为什么我写的代码能行而你写的就有问题呢?主要是因为当你调用GetName时返回的CString对象是个临时对象。参见:《CReferencemanual》§  在一些环境中编译器有必要创建一个临时对象这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话编译器要确保这个类的构造函数被调用。同样的如果这个类声明有析构函数的话也要确保这个临时对象的析构函数被调用。  编译器必须确保这个临时对象被销毁了。被销毁的确切地点依赖于实现这个析构函数必须在退出创建该临时对象的范围之前被调用。  大部分的编译器是这样设计的:在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数实现起来一般都是在下一个分号处。因此这个CString对象在GetBuffer调用之后就被析构了(顺便提一句你没有理由给GetBuffer函数传递一个参数而且没有使用ReleaseBuffer也是不对的)。所以GetBuffer本来返回的是指向这个临时对象中字符串的地址的指针不过当这个临时对象被析构后这块内存就被释放了。然后MFC的调试内存分配器会重新为这块内存全部填上xDD显示出来刚好就是“Yacute”符号。在这个时候你向注册表中写数据字符串的内容当然全被破坏了。  我们不应该即时把这个临时对象转化成char*类型应该先把他保存到一个CString对象中这意味着把临时对象复制了一份所以当临时的CString对象被析构了之后这个CString对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。  此外我的代码是具有Unicode意识的。那个操作注册表的函数需要一个字节大小使用lstrlen(Name)得到的实际结果对于Unicode字符来说比ANSI字符要小一半而且他也不能从这个字符串的第二个字符起开始计算也许你的本意是lstrlen(Name)(OK我承认我也犯了同样的错误!)。不论怎么在Unicode模式下所有的字符都是个字节大小我们需要处理这个问题。微软的文件令人惊讶地对此保持缄默:REGSZ的值究竟是以字节计算还是以字符计算呢?我们假设他指的是以字节为单位计算你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。、CString的效率  CString的一个问题是他确实掩藏了一些低效率的东西。从另外一个方面讲他也确实能被实现得更加高效你

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/17

VC++_CString的操作

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利