竞赛:| 全国大学生数模竞赛 | 全国研究生数模竞赛 | 全国大学生电工数模竞赛 | 美国"MCM/ICM" 竞赛 |
 资讯:| 数学理论 | 交叉学科 | 基础教育 | 考研数学 | 学术动态 | 编程交流 | 网络安全 | 经验技巧 |
 下载:| 数 学 篇 | 算 法 篇 | 建 模 篇 | 编 程 篇 | 数 据 篇 | 软 件 篇 | 考 研 篇 | 交叉学科 |
 视频:| 大学数学 | 大学英语 | 计 算 机 | 法律课程 | 政治课程 | 经济管理 | 数学建模 | 高考数学 |
 论坛:| 建模竞赛 | 学校协会 | 资源交流 | 数学门类 | 数学软件 | 管理应用 | 论坛公告 | 娱乐论坛 |


 
会员中心
社区论坛
加入收藏
联系我们
您现在的位置: 数学中国 >> 资讯无限 >> 计算机技术 >> 经验技巧 >> 文章正文
【字体:           ★★
 
CString Management
作者:未知    文章来源:本站原创    点击数:    更新时间:2006-10-30

                                                CString Management

 

  CStrings are a useful data type. They greatly simplify a lot of operations in MFC, making it much more convenient to do string manipulation. However, there are some special techniques to using CStrings, particularly hard for people coming from a pure-C background to learn. This essay discusses some of these techniques.

Much of what you need to do is pretty straightforward. This is not a complete tutorial on CStrings, but captures the most common basic questions. 

String Concatenation

One of the very convenient features of CString is the ability to concatenate two strings. For example if we have

CString gray("Gray");

CString cat("Cat");

CString graycat = gray + cat;

is a lot nicer than having to do something like:

char gray[] = "Gray";

char cat[] = "Cat";

char * graycat = malloc(strlen(gray) + strlen(cat) + 1);

strcpy(graycat, gray);

strcat(graycat, cat);

Formatting (including integer-to-CString)

Rather than using sprintf or wsprintf, you can do formatting for a CString by using the Format method:

CString s;

s.Format(_T("The total is %d"), total);

The advantage here is that you don't have to worry about whether or not the buffer is large enough to hold the formatted data; this is handled for you by the formatting routines.

Use of formatting is the most common way of converting from non-string data types to a CString, for example, converting an integer to a CString:

CString s;

s.Format(_T("%d"), total);

I always use the _T( ) macro because I design my programs to be at least Unicode-aware, but that's a topic for some other essay. The purpose of _T( ) is to compile a string for an 8-bit-character application as:

#define _T(x) x // non-Unicode version

whereas for a Unicode application it is defined as

#define _T(x) L##x // Unicode version

so in Unicode the effect is as if I had written

s.Format(L"%d", total);

If you ever think you might ever possibly use Unicode, start coding in a Unicode-aware fashion. For example, never, ever use sizeof( ) to get the size of a character buffer, because it will be off by a factor of 2 in a Unicode application. We cover Unicode in some detail in Win32 Programming. When I need a size, I have a macro called DIM, which is defined in a file dim.h that I include everywhere:

#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )

This is not only useful for dealing with Unicode buffers whose size is fixed at compile time, but any compile-time defined table.

class Whatever {...};Whatever data[] = {  {...},    ...   {...},};for(int i = 0; i < DIM(data); i++) // scan the table looking for a match

Beware of those API calls that want genuine byte counts; using a character count will not work.

TCHAR data[20];lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!lstrcpyn(data, longstring, DIM(data) - 1); // RIGHTWriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT

This is because lstrcpyn wants a character count, but WriteFile wants a byte count. Also note that this always writes out the entire contents of data. If you only want to write out the actual length of the data, you would think you might do

WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG

but that will not work in a Unicode application. Instead, you must do

WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT

because WriteFile wants a byte count. (For those of you who might be tempted to say "but that means I'll always be multiplying by 1 for ordinary applications, and that is inefficient", you need to understand what compilers actually do. No real C or C++ compiler would actually compile a multiply instruction inline; the multiply-by-one is simply discarded by the compiler as being a silly thing to do. And if you think when you use Unicode that you'll have to pay the cost of multiplying by 2, remember that this is just a bit-shift left by 1 bit, which the compiler is also happy to do instead of the multiplication).

Using _T does not create a Unicode application. It creates a Unicode-aware application. When you compile in the default 8-bit mode, you get a "normal" 8-bit program; when you compile in Unicode mode, you get a Unicode (16-bit-character) application. Note that a CString in a Unicode application is a string that holds 16-bit characters.

Converting a CString to an integer

The simplest way to convert a CString to an integer value is to use one of the standard string-to-integer conversion routines.

While generally you will suspect that _atoi is a good choice, it is rarely the right choice. If you play to be Unicode-ready, you should call the function _ttoi, which compiles into _atoi in ANSI code and _wtoi in Unicode code. You can also consider using _tcstoul (for unsigned conversion to any radix, such as 2, 8, 10 or 16) or _tcstol (for signed conversion to any radix). For example, here are some examples:

CString hex = _T("FAB");

CString decimal = _T("4011");

ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));

Converting between char * and CString

This is the most common set of questions beginners have on the CString data type. Due largely to serious C++ magic, you can largely ignore many of the problems. Things just "work right". The problems come about when you don't understand the basic mechanisms and then don't understand why something that seems obvious doesn't work.

For example, having noticed the above example you might wonder why you can't write

CString graycat = "Gray" + "Cat";

or

CString graycat("Gray" + "Cat");

In fact the compiler will complain bitterly about these attempts. Why Because the + operator is defined as an overloaded operator on various combinations of the CString and LPCTSTR data types, but not between two LPCTSTR data types, which are underlying data types. You can't overload C++ operators on base types like int and char, or char *. What will work is 

CString graycat = CString("Gray") + CString("Cat");

or even

CString graycat = CString("Gray") + "Cat";

If you study these, you will see that the + always applies to at least one CString and one LPCSTR.

char * to CString

So you have a char *, or a string. How do you create a CString. Here are some examples:

char * p = "This is a test"

or, in Unicode-aware applications

TCHAR * p = _T("This is a test")

or

LPTSTR p = _T("This is a test");

you can write any of the following:

CString s = "This is a test";     // 8-bit only

CString s = _T("This is a test"); // Unicode-aware

CString s("This is a test");      // 8-bit only

CSTring s(_T("This is a test");   // Unicode-aware

CString s = p;

CString s(p);

Any of these readily convert the constant string or the pointer to a CString value. Note that the characters assigned are always copied into the CString so that you can do something like

TCHAR * p = _T("Gray");

CString s(p);

p = _T("Cat");

s += p;

and be sure that the resulting string is "GrayCat".

There are several other methods for CString constructors, but we will not consider most of these here; you can read about them on your own.

CString to char * I: Casting to LPCTSTR

This is a slightly harder transition to find out about, and there is lots of confusion about the "right" way to do it. There are quite a few right ways, and probably an equal number of wrong ways.

The first thing you have to understand about a CString is that it is a special C++ object which contains three values: a pointer to a buffer, a count of the valid characters in the buffer, and a buffer length. The count of the number of characters can be any size from 0 up to the maximum length of the buffer minus one (for the NUL byte). The character count and buffer length are cleverly hidden.

Unless you do some special things, you know nothing about the size of the buffer that is associated with the CString. Therefore, if you can get the address of the buffer, you cannot change its contents. You cannot shorten the contents, and you absolutely must not lengthen the contents. This leads to some at-first-glance odd workarounds.

The operator LPCTSTR (or more specifically, the operator const TCHAR *), is overloaded for CString. The definition of the operator is to return the address of the buffer. Thus, if you need a string pointer to the CString you can do something like

CString s("GrayCat");

LPCTSTR p =  s;

and it works correctly. This is because of the rules about how casting is done in C; when a cast is required, C++ rules allow the cast to be selected. For example, you could define (float) as a cast on a complex number (a pair of floats) and define it to return only the first float (called the "real part") of the complex number so you could say

Complex c(1.2f, 4.8f);

float realpart = c;

and expect to see, if the (float) operator is defined properly, that the value of realpart is now 1.2.

This works for you in all kinds of places. For example, any function that takes an LPCTSTR parameter will force this coercion, so that you can have a function (perhaps in a DLL you bought):

BOOL DoSomethingCool(LPCTSTR s);

and call it as follows

CString file("c:\myfiles\coolstuff")

BOOL result = DoSomethingCool(file);

This works correctly because the DoSomethingCool function has specified that it wants an LPCTSTR and therefore the LPCTSTR operator is applied to the argument, which in MFC means that the address of the string is returned.

But what if you want to format it

CString graycat("GrayCat");

CString s;

s.Format("Mew! I love %s", graycat);

Note that because the value appears in the variable-argument list (the list designated by "..." in the specification of the function) that there is no implicit coercion operator. What are you going to get

Well, surprise, you actually get the string

"Mew! I love GrayCat"

because the MFC implementers carefully designed the CString data type so that an expression of type CString evaluates to the pointer to the string, so in the absence of any casting, such as in a Format or sprintf, you will still get the correct behavior. The additional data that describes a CString actually lives in the addresses below the nominal CString address.

What you can't do is modify the string. For example, you might try to do something like replace the "." by a "," (don't do it this way, you should use the National Language Support features for decimal conversions if you care about internationalization, but this makes a simple example):

CString v("1.00");  // currency amount, 2 decimal places

LPCTSTR p = v;

p[lstrlen(p) - 3] = ',';

If you try to do this, the compiler will complain that you are assigning to a constant string. This is the correct message. It would also complain if you tried

strcat(p, "each");

because strcat wants an LPTSTR as its first argument and you gave it an LPCTSTR

Don't try to defeat these error messages. You will get yourself into trouble!

The reason is that the buffer has a count, which is inaccessible to you (it's in that hidden area that sits below the CString address), and if you change the string, you won't see the change reflected in the character count for the buffer. Furthermore, if the string happens to be just about as long as the buffer physical limit (more on this later), an attempt to extend the string will overwrite whatever is beyond the buffer, which is memory you have no right to write (right) and you'll damage memory you don't own. Sure recipe for a dead application.

CString to char * II: Using GetBuffer

A special method is available for a CString if you need to modify it. This is the operation GetBuffer. What this does is return to you a pointer to the buffer which is considered writeable. If you are only going to change characters or shorten the string, you are now free to do so:

CString s(_T("File.ext"));

LPTSTR p = s.GetBuffer();

LPTSTR dot = strchr(p, '.'); // OK, should have used s.Find...

if(p != NULL)

 *p = _T('

 

文章录入:babybluema    责任编辑:madio  
  • 上一篇文章:

  • 下一篇文章:
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    推 荐 文 章
    更多内容
     
    热 门 文 章  
    更多内容
     

    关于鼓励研究生深入…
    相 关 文 章
    更多内容
     
    没有相关文章
    | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 管理登录 |