CSTRING Operation Guide
Original source: CodeProject: cstring management
By reading this article You can learn how to effectively use CString.
CString is a very useful data type. They greatly simplify many of the operations in the MFC, making MFCs convenient when making a string operation. In any case, there are many special skills using CString, especially for programmers coming out of pure C background. This article discusses these techniques. Using CString allows you to operate more straightforward. This article is not a complete manual of CString, but there is most common basic issues.
This article includes the following:
CSTRING connection
Format strings (including int type transformation to cstract) CSTRING-type transformation into int type CString type and char * type
Char * Convert CString to CHAR *: Using LPCTSTR to force convert CString into char * 2: Translate into a CSTRING object CString into char * three: and control interface CSTRING transformation into BSTR type; The BSTR type is converted into a CString type; Varian type is converted into a CString type; loading a string table resource; CSTRING and temporary objects; CString efficiency; summarize I discussed below.
1, CString object connection
It can reflect an aspect of the CSTRING type of convenient feature, using the CString type, you can easily connect two strings, just as follows:
CSTRING GRAY ("gray"); cstring cat ("cat"); cstract graycat = gray cat; more than the method below:
Char gray [] = "gray"; char cat [] = "cat"; char * graycat = malloc (gray) Strlen (CAT) 1); strcpy (graycat, gray); strcat (graycat, cat) ; 2, formatted strings
Instere, a string is formatted with a sprintf () function or a wsprintf () function, it is better to use the format () method of the CSTRING object:
CString S; S.Format (_T ("The Total IS% D"), TOTAL); The advantage of this method is whether you don't have to worry about whether the buffer used to store the formatted data is large enough, these work by the CSTRING class Complete it for you. Format is the most common technique that converts other data that is not a string type to a cString type, for example, transforming an integer into a CString type, which can be used as follows:
CString S; S.Format (_T ("% D"), Total); I am always using the character string _t () macro, this is to make my code at least Unicode consciousness, of course, about Unicode The topic is not discussed in this article. _T () macro is defined below in the 8-bit character environment:
#define _t (x) x // non-Unicode version (Non-Unicode Version) is as defined in the Unicode environment:
#define _t (x) L ## x // Unicode version So in the Unicode environment, its effect is equivalent to: S.Format (L "% D", Total); if you think your program It may be run in a Unicode environment, then starting in the application Unicode encoding. For example, don't use the sizeof () operator to get the length of the string, because there will be twice the error in the Unicode environment. We can use some ways to hide some details of Unicode. For example, when I need to get the character length, I will use a macro called Dim, this macro is defined in my Dim.h file, I will write in me. All programs include this file:
#DEFINE DIM (X) (SIZEOF ((X)) / sizeof ((x))) This macro not only can be used to solve the problem of Unicode's string length, or it can be used on a table defined at compile time. It can get the number of items in the table, as follows: class whatver {...}; whatver data [] = {{...}, ... {...},}; for (int i = 0; i < DIM (DATA); i ) // Scan table looks for match.
Here, you must remind you that you must pay attention to the API function calls that need the real/4 of the character in the parameter. If you pass the characters, it will not work properly. As follows: Tchar Data [20]; LSTRCPYN (Data, Longstring, Sizeof (Data) - 1); // Wrong! LSTRCPYN (Data, LongString, Dim (Data) - 1); // RightWriteFile (f, Data, Dim) DATA), & BYTESWRITEN, NULL); // Wrong! Writefile (f, data, sizeof (data), & byteswritten, null); // Right causes the above reason because lstrcpyn needs a character number as a parameter, but writefile needs the word The number of times is used as a parameter. It is also important to note that all contents of data need to be written. If you just want to write the real length of the data, you may think you should do this:
Writefile (F, Data, LSTRLEN (DATA), & BYTESWRITEN, NULL); // WRONG but in a Unicode environment, it will not work properly. The correct approach should be like this:
Writefile (f, data, lstrlen (data) * sizeof (tchar), & Byteswritten, null; // Right Because WriteFile needs a length of byte. (Some people may want to "run this line of code in a non-Unicode environment, which means always doing a redundant 1 operation, this is not to reduce the efficiency of the program?" This idea is extra, you You must understand what the compiler does actually do, no C or C compiler will leave this boring multiplication in the code. When you run in the Unicode environment, you don't have to worry about the 2 operation. Reducing the efficiency of the program, remember, this is just a left shift one, edging 饕 芾 芾 馕 馕 阕 婊 #? Br> Using _t macro does not mean you have created a Unicode's program, you just create a program with Unicode consciousness. If you compile your program in the default 8-bit mode, you get an ordinary 8-bit application (8-8- Bit refers to the 8-bit character encoding, not to refer to the 8-bit computer system); when you compile your program in a Unicode environment, you will get a Unicode program. Remember, CSTRING In the Unicode environment, It can be 16-bit characters in it .3, cstring type translates into int type
Transforming the data of the CString type into an integer type is the simpler method to use a standard string to an integer conversion routine. Although you usually doubt using the _atoi () function is a good choice, it is rarely a correct choice. If you are ready to use Unicode characters, you should use _ttoi (), which is compiled into _atoi () in the ANSI coding system, and compiles _wtoi () in the Unicode encoding system. You can also consider using _tcstoul () or _tcstol (), they can convert the string into any long intensity of arbitrary evolution (such as binary, octal, decimal or hexadecimal), and different points lies after transformation of the former The data is unsigned, and the latter is the opposite. Look at the example below:
CString Hex = _t ("fab"); cstring decimal = _t ("4011"); assert (_tcstoul (hex, 0, 16) == _ttoi (decimal)); 4, CSTRING type and char * type mutual conversion
This is the most common problem when beginners use CString. With the help of C , many questions you don't need to consider it, take it directly, but if you can't understand its running mechanism, there will be a lot of problems to make you confused, especially some seem to have no The code of the problem is not working properly. For example, you will be strange why you can't write the code like this:
CString graycat = "gray" "cat"; or this:
CSTRING GRAYCAT ("Gray" "CAT"); in fact, the compiler will complain that these attempts are. why? The " " operator is defined as an overload operator for a wide variety of combinations of CString and LPCTSTR data types. Instead of two LPCTSTR data types, it is the underlying data type. You cannot overload C operators for basic data (such as int, char or char *) type. You can do this below: cstring graycat = cstring ("gray") CString ("cat"); or this:
CString graycat = cstring ("gray") "cat"; "Gray" will find: " " always uses at least a CString object and an LPCSTR.
Note that the code to write Unicode is always a good thing, such as:
CString graycat = cstring (_t ("gray")) _T ("cat"); this will make your code directly transplant.
Char * translates to cstring
Now you have a char * type data, or a string. How to create a CSTRING object? Here are some examples:
Char * p = "this is a test"; or like this more unicode consciousness:
TCHAR * P = _t ("this is a test") or
LPTSTR P = _t ("this is a test"); you can use any of the following:
CString S = "this is a test"; // 8-bit online); // unicode-awarecstring s ("this is a test"); // 8-bit OnlyCString S (_T ("this is a test")); // unicode-awarecstring s = p; cstring s (p); use these methods to easily convert constant strings or pointers to cstring. It should be noted that the assignment of characters is always copied to the CString object, so you can do this below:
TCHAR * P = _T ("gray"); cstring s (p); p = _t ("cat"); s = p; result string is definitely "graycat".
There are several other constructor, but we don't consider it, if you are interested, you can view the relevant document yourself.
In fact, the constructor of the CSTRING class is complicated than I show, such as:
CString s = "this is a test"; this is a hormible code, but in fact it can compile in a Unicode environment. It converts the 8-bit string into a 16-bit string when the multibyToWideChar operation calls the constructor. Anyway, if the char * pointer is 8-bit data transmitted on the network, this conversion is useful.
CString is converted to one of Char *: Mandatory types are converted to LPCTSTR; this is a slightly hardened conversion. There are still many confused, and there are many correct ways, but the use of mistakes Methods may be as much as the correct use. We must first learn that CString is a very special C object, which contains three values: a pointer to a data buffer, one is a valid character count in this buffer and a buffer length. The size of the effective character may be any number of 0. 0 to this buffer maximum length value (because the string ends with a NULL character). The character count and the buffer length are smartly. Unless you do some special operations, you can't know the length of the buffer assigned to the CString object. In this way, even if you get the 0 buffer address, you can't change the content, you can't truncate the string, and there is absolutely no way to extend its content, otherwise it will see overflow. The LPCTSTR operator (or more specifically that tchar * operator) is overloaded in the CSTRING class, the definition of the operator is the address of the buffer, so if you need a string pointer pointing to CString, Can do this:
CSTRING S ("graycat"); lpctstr p = s; it can run correctly. This is achieved by the Cultural Cultural Circulation Rules. When the mandatory type is required, C rules allow this option. For example, you can define (floating point) to convert a plural (a pair of floating point numbers), only returning the first floating point number (which is actually). Can be like this:
Complex C (1.2F, 4.8F); FLOAT REALPART = C; if the FLOAT operator defines correct, then the value of the real part should be 1.2. This forced transformation is suitable for all this, for example, any function with the LPCTSTR type parameter enforces this conversion. So you may have such a function (maybe in a DLL you bought):
Bool Dosomethingcool (LPCTSTR S); you call it like this:
CSTRING FILE ("C: // myfiles // coolstuff") Bool Result = DOSMETHINGCOOL (file); it can run correctly. Because the DOSMETHINGCOOL function has explained a parameter that requires an LPCTSTR type, LPCTSTR is applied to this parameter, and in the MFC is the string address returned.
What if you want to format a string?
CString graycat ("graycat"); cstring s; s.format ("MEW! I Love% S", graycat; note due to the value in the variable parameter list (in function description "..." The) does not imply a forced type conversion operator. What will you get? An amazing result, the actual result string we get is:
"MEW! I Love Graycat". Because the MFC designers are very careful when designing the CSTRING data type, the cstring type expression begins to point to the string after evaluation, so you can't see the mandatory type conversion in Format or Sprintf, you can still get the right behavior. . Describe the additional data of CString is actually after the CString name address. One thing you can't do, that is to modify the string. For example, you may try to use "," instead "." (Don't do this, if you care about the internationalization, you should use the National Language Support feature of decimal conversion,), below is a simple example: cstring v (" 1.00 "); // Money amount, two decimal lpctstr p = v; p [lstrlen (p) - 3] =,; then the compiler will report an error because you assign a constant string. If you try the following, the compiler will also be wrong:
STRCAT (P, "EACH"); because Strcat's first parameter should be the LPTSTR type data, but you give an LPCTSTR.
Don't try to diamond this error message, this will only make you in trouble!
The reason is that the buffer has a count, which is an unbearable (it is located in a hidden area under the cstring address), if you change this string, the buffer is not reflected in the modification. In addition, if the string length is exactly the length of the physical limit of the string (this problem will also be said), then the string will override any data other than the buffer, which is the memory you have no right to write. Not right?), You will destroy the change is not your memory. This is the real death prescription of the application.
CString translates into char * 2: using the getBuffer method of the CString object;
If you need to modify the content in the cstring, it has a special method to use, which is getBuffer, its role is to return a writable buffer pointer. If you just want to modify characters or truncated strings, you can do this:
CString S ("file.ext"); lptstr p = s.getBuffer (); lptstr dot = strchr (p,.); // ok, shouth hased s.find ... if (p! = NULL) * P = _t (/ 0); s.releaseBuffer (); this is the first usage of getBuffer, is also the simplest one, does not deliver parameters, it uses default 0, meaning: "give I have a pointer of this string, I don't lose it. " When you call ReleaseBuffer, the actual length of the string is recalculated and then stored in the CString object. It must be emphasized that this range between GetBuffer and ReleaseBuffer must not use any method of this buffered CString object you want to operate. Because ReleaseBuffer is called, the integrity of the CSTRING object is not guaranteed. Take the following code:
CString s;
LPTSTR P = S.GetBuffer ();
// ... this pointer P has a lot of things
INT n = s.GetLength (); // is very bad D !!!!! It is possible to give a wrong answer !!! s.trimright (); // is very bad !!!!! Can not guarantee normal work! !!!
s.releaseBuffer (); // should now be OK
INT m = s.getLength (); // This result can be guaranteed correctly.
S.trimright (); // will work normally. Suppose you want to increase the length of the string, you first have to know how long this string is, comparison is when the string array is declared:
Char buffer [1024]; 1624 characters are sufficient to make you do anything to do. A representation of the meaning of the meaning in cstring:
LPTSTR P = S.GetBuffer (1024); After calling this function, you not only get the pointer of the string buffer, but also a space for at least 1024 characters in length (note, I am talking about "characters", Instead of "bytes" because cstring is implied by Unicode). At the same time, it should also be noted that if you have a constant string pointer, the value of this string itself is stored in read-only memory, if you try to store it, even if you have called GetBuffer, get a pointer to read-only memory, The deposited operation will fail and report an access error. I didn't prove this on CString, but I saw that the big C programmer often made this mistake. C programmers have a common problem to assign a fixed length buffer that performs sprintf operations, and assigns it to a cstring:
CHAR BUFFER [256]; Sprintf (buffer, "% ...", args, ...); // ... part omit a lot of details CString s = Buffer; although better forms can be said:
CString S; S.Format (_T ("% ...."), args, ...); if your string length exceeds 256 characters, the stack will not be destroyed.
Another common mistake is: Since the fixed size memory does not work, then use dynamic allocation bytes, this approach is more short:
INT LEN = LSTRLEN (PARM1) 13 lstrlen (PARM2) 10 100;
Char * buffer = new char [len];
Sprintf (Buffer, "% s is equals to% s, valid data", parm1, parm2);
Cstring s = buffer;
......
Delete [] buffer; it can be simply written:
CString S;
S.Format ("% s is equal to% s, valid data"), PARM1, PARM2); Need to note that the sprintf example is not unicode, although you can use Tsprintf and use _t () to enclose formatting String, but basic ideas are still walking, this is easy to make mistakes.
Three cstring to char *: interface between the control;
We often need to pass a CString value to a control, such as CTREECTRL. MFC provides us with a lot of convenience to overload this operation, but in most cases, you use the "original" form update, so you need to store a string pointer to the TVItemstruct structure of the TVInsertItemstruct structure. As follows: TVINSERTITEMSTRUCT TVI; cstring s; // ... is assigned to S. TVi.Item.psztext = s; // compiler Yells at you here // ... Fill in the other domain of TVi HtreeItem Ti = C_Mytree.InsertItem (& TVi); Why will the compiler error? It is a perfect usage! But in fact, if you look at the definition of the TVItem structure, you will understand that in the TVITEM structure, the statement of the PSZText member is as follows:
LPTSTR PSZTEXT; INT CCHTextMax; therefore, assignment does not assign a variable to an LPCTSTR type, and the compiler cannot know how to convert the assignment statement to LPCTSTS. Ok, you said, then I will change this:
TVi.Item.psztext = (lpctstr) s; // The compiler will still report an error. The compiler is still an error because you attempt to assign a variable of a LPCTSTR type to a LPTSTR type variable, which is disabled in C or C . You can't use this method to abuse constant pointers and very quantitative pointer concepts, otherwise, you will disturb the optimization mechanism of the compiler, so that you don't know how to optimize your procedure. For example, if you do this:
Const Int i = ...; // ... do lots of stuff ... = a; // usage 1 // ... Lots more stuff ... = a; // usage 2, then compiler Since I is const, the value of USAGE1 and USAGE2 is the same, and it can even calculate the address at the USAGe1 in advance, and then reserve it in the next USAGE2 instead of recalculation. If you write it as follows:
Const Int i = ...; INT * P = & I; // ... do lots of stuff ... = a; // usage 1 // ... LOTS more stuff (* p) ; // Mess over compilers assumption // ... and other stuff ... = a; // usage 2 The compiler will believe that I is constant, so that the position of A is constant, which indirectly destroyed previous hypotheses. Therefore, your program will reflect different behaviors in debug compilation mode (not optimized) and release compilation mode (fully optimized), this situation is not good, so when you try to assign a pointer to i to a modification When the reference is quoted, it will be diagnosed with the compiler to this is a forgery. That's why (LPCTSTR) mandatory reasons do not work. Why don't you declare this member into a lpctstr type? Because this structure is used to read and write controls. When you write data to the control, the text pointer is actually treated as LPCTSTR, and when you read the data from the control, you must have a writable string. This structure cannot distinguish it is used to read or use it.
So you will often see the following usage in my code: TVi.Item.psztext = (lpctstr) s; it converts the CString mandatory type into lpctstr, that is, the address of the change string first, Then forced the type to translate into LPTSTR to assign it to it. Note that this is only valid in the method such as SET or INSERT! If you try to get data, you can't do this. If you plan to get the data stored in the control, the method is slightly different, for example, using the GetItem method for a CTREECTRL, I want to get the text of the project. I know that these texts will not exceed my_limit, so I can write this:
TVItem TVi; // ... assorted initialization of other fields of tvitvi.psztext = s.getBuffer (my_limit); tvi.cchtextmax = my_limit; c_mytree.getitem (& TVi); s.releasebuffer (); can be seen, actually above The code applies to all types of SET methods, but don't do it because all class SET methods (including INSERT methods) do not change the contents of the string. But when you need to write a CString object, you must ensure that the buffer is writable, this is what getBuffer does. Again again: Once a getBuffer call is made, do not do anything about this CString object before calling ReleaseBuffer.
5, CSTRING type conversion to BSTR type
When we use the ActiveX control programming, you often need to use a value as a BSTR type. BSTR is a string, a wide string (Unicode) on the Intel platform, and can contain embedded NULL characters.
You can call the unlocsystring method of the CString object to convert the cString into BSTR:
CString S; s = ...; // whereverbstr b = s.allocsystring (); Now the pointer B pointing is a newly allocated BSTR object, which is a copy of CString, which contains the termination null character. Now you can pass it to any interface that requires BSTR. Typically, BSTR is released by the components that receive it. If you need to release BSTR, you can do this:
:: sysfreestring (b); for how to express the string passing to the ActiveX control, I am arguing in Microsoft, and finally Visual Basic's people account for the wind, BSTR ("Basic String" clubs) is this debate the result of.
6, BSTR type conversion to cstring type
Since BSTR is a count Unicode string, you can create 8-bit CString with a standard conversion method. In fact, this is a CString built-in function. There is a special constructor in CString to convert the ANSI to Unicode or convert Unicode into ansi. You can also get the BSTR type string from the variant type variable, and the Variant type is the type returned by various COM and Automation (Automation).
For example, in an ANSI program:
BSTR B; B = ...; // WhatverCString S (b) ": b) For a single BSTR string, this usage can work very well, because cstring has a special The constructor is parameter in lpcwstr (BSTR is this type) and transforms it into an ANSI type. Special inspection is necessary, because the BSTR may be null, and the CString constructor is not very thoughtful for NULL values, (thank Brian Ross to point out this!). This usage can only handle a single string containing NUL termination characters; if you want to convert multiple NULL strings, you have to do some work. NULL characters embed in CString usually do not agree, should be avoided as much as possible. According to C / C rules, if you have a LPWSTR, then it doesn't choose, you can only match the LPCWSTR parameter. In Unicode mode, its constructor is:
CString :: CString (LPCTSTST); As shown above, in ANSI mode, it has a special constructor:
CString :: CSTRING (LPCWSTR); it calls an internal function to convert the Unicode string into an ANSI string. (In Unicode mode, there is a dedicated constructor, which has a parameter is a LPCSTR type - a 8-bit ANSI string pointer, which is widened to Unicode string!) Emphasis: Be sure to check Is the value of BSTR is NULL. There is another problem, as mentioned above: BSTRS can contain multiple embedded NULL characters, but the CString constructor can only handle a single NULL character in a string. That is, if the string contains embedded NUL bytes, CString will calculate the bundle string length. You must handle it yourself. If you look at the constructor in strcore.cpp, you will find that they all call lstrlen, which is the length of the string. Note that from Unicode to ANSI conversion uses special parameters: WideChartomultibyte, if you don't want to use this default conversion method, you must write your own conversion code. If you compile the code in Unicode mode, you can simply write:
CSTRING Convert (BSTR B) {if (b == null) Return CString (_t (")); cstring s (b); // in unicode modereturn s;}
If it is an ANSI mode, a more complex process is required to convert. Note this code uses the same parameter value as: widechartomultibyte. So you can only use this technology when you want to change these parameters. For example, specify different default characters, different logos sets, and the like. CString Convert (BSTR B) {CSTRING S; if (b == null) Return S; // Empty for null bstr # ifdef unicodes = B; #ELSLPSTR P = S.GetBuffer (Systringlen (b) 1); :: WideCharToMultiByte (CP_ACP, // ANSI Code Page0, // no flagsb, // source widechar string-1, // assume NUL-terminatedp, // target bufferSysStringLen (b) 1, // target buffer lengthNULL, // use system Default Charnull; // DONT CARE IF DEFAULT Useds.releaseBuffer (); # endifreturn s;} I don't worry what happens if the BSTR contains Unicode characters that are not mapped to the 8-bit character set, because I specified :: WideChartomultibyte The last two parameters are NULL. This is where you may need to change.
7, Variant type transformation into a CSTRING type
In fact, I have never done it because I have not written in the process of writing in COM / OLE / ActiveX. But I saw a post on the Microsoft.Public.vc.mfc newsgroup to talk about this conversion, I think it is not very good to put his article in my article, so more here Do some explanations and demonstrations. If there is a place with his article, it may be my negligence. Variant types are often used to deliver parameters to COM objects or receive values returned from COM objects. You can also write it back to the Variant type method, the function returns what type of input parameters depend on the possible (and often) method (for example, in the automation operation, depending on which method is called with you, which method can be returned (by one Parameters) A result containing Byte, Word, Float, Double, Date, BSTR. (For details on the definition of the Variant structure on the MSDN). In the following example, assuming type is a BSTR variant, That is to say, the value in the string is referenced by BSRTVAL. It has the advantage that in the ANSI application, there is a constructor to convert the value of the LPCWCHAR reference to a cString (see the BSTR-to-CString section). In Unicode mode It will become a standard CString constructor, see the warning of the default :: widechartomultibyte conversion, and you feel acceptable (in most cases, you will be satisfied) .variant vadata; vadata = m_com.Yourmethodhere (); askERT (VADATA.VT == Vt_BSTR); CString strdata (VADATA.BSTRVAL); you can also establish a more versatile conversion routine according to the VT domain. To this end, you may consider:
CString VariantTostring (Variant * VA) {CString S; Switch (VA-> VT) {/ * vt * / case vt_bstr: return cstring (vadata-> bstrval); case vt_bstr | vt_byref: return cstring (* vadata-> pbstrval) Case vt_i4: s.format (_T ("% D"), VA-> LVAL); RETURN S; CASE VT_I4 | VT_BYREF: S.Format (_T ("% D"), * VA-> PLVAL); case Vt_r8: s.format (_t ("% f"), VA-> DBLVAL); return s; ... The remaining type conversion by the reader to complete Default: assert (false); // unknown variant type (this assert Is Optional) Return CSTRING ("");} / * vt * /} 8, load string resource
If you want to create an app that is easy to make language-transplanted applications, you cannot directly include local language strings (below these examples, because my local language is English), For example, this kind of writing is worse: cstring s = "there is an error"; you should put all the strings of all your specific languages (except for information that does not appear in the release version). This means that it is better to write this way:
S.Format ("% D -% s"), code, text); In your program, text strings are not language sensitive. Anyway, you have to be very careful, don't use the string of the following:
// fmt is "error in% s file% s" // readorwrite is "Reading" or "Writing" S.Format (FMT, ReadorWrite, FileName); this is my personal experience. In my first international app, I made this mistake. Although I understand German, I know that in German grammar is placed in the end of the sentence, our German issuers are still suffering from them. Have to extract those incredible German error prompts and restrictions to make them work normally. A preferred way (also the way I use now) is to use two strings, one for reading, one for writing, load the appropriate version when using, so that they are non-sensitive to string parameters. That is to say, the entire format is loaded, not the load string "Reading": "Writing":
// fmt is "error in reading file% s" // "Error in Writing file% s" S.Format (FMT, filename); must pay attention to, if you have a few places need to be replaced, you must guarantee the replacement The structure of the sentence does not have problems, such as in English, can be the subject - object, subject-predicate, verb-object structure, etc. Here, we don't discuss formatmessage, in fact it has advantages over Sprintf / Format, but it is not easy to use CString. The way to solve this problem is that we will give the parameters in the parameter table in accordance with the parameters, so that they will not take their position wrong when you output it. Next we discuss us for these independent strings. We can put the value of the string into a section called StringTable in the resource file. The process is as follows: First, use the Visual Studio's resource editor to create a string, then give each character to a ID, usually we take the name of the name to start with IDS_. So if you have a message, you can create a string resource and name it IDS_READING_FILE, and the other is named IDS_Writing_File. They appear in your .rc file below: StringTableIDS_Reading_File "Reading File% S" IDS_Writing_File "Writing File% S" END Note: These resources are saved in Unicode's format, whether you compile it in what environment. They also exist in the form of Unicode in the Win9x system, although Win9x cannot really process Unicode. Then you can use these resources like this: // Before using the resource string table, the program is written this:
CSTRING FMT; if (...) FMT = "Reading File% S"; elsefmt = "Writing file% s"; ... // Much Latercstring S; S.Format (FMT, FileName);
// After using the resource string table, the program writes: cstring fmt; if (...) fmt.loadstring (ids_reading_file); Elsefmt.LoadString; ... // Much Latercstring S; S.Format (FMT, FileName);
Now, your code can be ported to any language. The LoadString method requires the ID of a string resource as a parameter, then it takes out its corresponding string from the StringTable to assign the value to the CSTRING object. The constructor of the CString object also has a smarter feature to simplify the use of StringTable. This usage is not pointed out in the CString :: CString document, but is used in the sample program of the constructor. (Why did this characteristics have become part of a formal document, but in an example, I can't remember!) - [Translator Note: From this sentence, the author may be a CString designer. In fact, there is still a similar thing in front. Said that he did not have a validity check to the address pointing to using getBuffer (0). This feature is: If you convert a string of resources to LPCTSTR, you will hibernate to call LoadString. Therefore, the following two configuration strings have the same effect, and its assert will not be triggered in Debug mode: CString S; S.LoadString (IDS_WHATEVER); CSTRING T ((LPCTSTR) IDS_WHATEVER; assert (s = = T); // will not be triggered, and S and T are the same. Now you may think: How can this work? How can we convert the StringTable ID into a pointer? Very simple: all string IDs are within 1 ~ 65535, that is, all of its highs are 0, and the pointer we used in the program is not less than 65535, because the program is 64K Memory will never exist. If you try to access the memory between 0000000000000000000FFF, a memory base error will be triggered. Therefore, the value of 1 ~ 65535 cannot be a memory address, so we can use these values as the ID of the string resource. I tend to use the MakeintResource macro to make this conversion. I think this can make the code easier to read. This is a standard macro that is only suitable for use in MFC. You have to remember that most methods can accept a UINT type parameter, or accept one LPCTSTR type parameter, which is done depending on the overload function of C . The drawbacks brought by the C overload function are to cause all mandatory types of transformations that need to be displayed. Similarly, you can only deliver a resource name for many structures. CString S; S.LoadString (IDS_WHATEVER); CString T (MakeintResource (IDS_WHATEVER)); assert (s == t); tell you: I am just advocating here, but I do this. In my code, you almost impossible to find a string, of course, except those that are just accidentally or unrelated to the language in debugging.
9, cstring and temporary objects
This is a small problem that appears in the Microsoft.Public.vc.mfc newsgroup, I simply put it, this question is to write a string in the registry, he wrote: I tried it RegSetValueex () Sets the value of a registry key, but its result is always confused. When I declare a variable when I use a variable, it works fine, but when I use CString, I always get some garbage: "YYYY ... YYYYYY" For confirmation, I have a problem, I tried Use GetBuffer and then force to convert to CHAR *, LPCSTR. GetBuffer returned to the correct, but when I assigned it to char *, it turned garbage. The following is my block: char * szname = getName (). GetBuffer (20); RegSetValueex (HKEY, "Name", 0, REG_SZ, STRLEN (SZNAME 1)); this Name character The length of the string is less than 20, so I don't think it is a problem with the parameters of getBuffer.
Really confused, please help me.
Dear Frustrate,
You made a quite subtle mistake, smart and smart, correct code should be like this:
CString name = getName (); RegSetValueex (HKEY, _T ("name"), 0, reg_sz, (const byte *) (lpctstr) Name, (Name.getLength () 1) * sizeof (tchar));
Why do I have a problem with the code you write? Mainly because the CString object returned when you call GetName is a temporary object. See: "C Reference Manual" §12.2 In some environments, the compiler is necessary to create a temporary object, which introduces the temporary object depending on the implementation. If this temporary object to which the compiler is introduced, the compiler ensures that the constructor of this class is called. Similarly, if this class declares that there is a destructor, it is also necessary to ensure that the destructor of this temporary object is called. The compiler must ensure that this temporary object is destroyed. The exact location destroyed by the destruction depends on implementation ..... This destructor must be called before exiting the range of the temporary object. Most compilers are designed: In the next execution step of the code created by the temporary object, I hidden the destructive function of this temporary object, which is realized, usually at the next semicolon. Therefore, this CString object is destructed after getBuffer calls (by way of manner, you have no reason to pass a parameter for the getBuffer function, and do not use ReleaseBuffer). So GetBuffer returned to the pointer to the address of the string in this temporary object, but after this temporary object is destructed, this memory is released. Then the MFC's debug memory distributor will be re-filled with this memory all 0xDD, which is displayed just "Y" symbol. At this time you write data to the registry, the content of the string is of course being ruined. We should not immediately transform this temporary object into a char * type. You should save it into a CString object, which means copying the temporary object, so after the temporary CSTRING object is destructed, this cstring The values in the object are saved. There is no problem to write data to the registry in the registry. In addition, my code is Unicode consciousness. The function of the operation registry requires a byte size. The actual result obtained by using lstrlen (Name 1) is half a half compared to the ANSI character than the ANSI character, and it cannot start from the second character of this string. Calculate, maybe your intention is lstrlen (name) 1 (OK, I admit that I also made the same mistake!). No matter how, in Unicode mode, all characters are 2 bytes, and we need to handle this problem. Microsoft's document is surprisingly keeping silent: REG_SZ is calculated in byte calculation with byte calculations? We assume that it is calculated in bytes, and you need to make some modifications to your code to calculate the byte size contained in this string. 10, cstring efficiency
One problem with CString is that it does hide some low efficiency. From another aspect, it can also be achieved more efficient, you may say the following code: cstring s = somecstring1; s = somecstring2; s = SomeCstring3; s = ","; s = SomeCstring4 The efficiency is much lower than the code below:
Char S [1024]; LSTRCPY (S, SomeString1); LSTRCAT (S, SomeString2); LSTRCAT (S, SomeString 3); LSTRCAT (S, ","); LSTRCAT (S, SomeString4); in short, you may want to think First, it assigns a piece of memory for someCString1, then copy someCString1 to the inside, then find it to be a connection, then redistribute a new enough big memory, big to the current string plus somecstring2, put the content Copy this memory, then connect someCString2 to the back, then release the first block, and re-point the pointer to the new memory. Then repeat this process for each string. How is more efficient to connect these four strings. In fact, in many cases, there is no need to copy the source string (the string on the left side of the = operator). In VC 6.0, in Release mode, the cache in all CString is allocated in predefined quantum. The so-called quantum is determined to be 64, 128, 256 or 512 bytes. This means unless the string is very long, the operation of the connection string is actually the optimized version (because it knows where the local string should end, so you don't need to look for the end of the string; just need memory The data is copied to the specified place to add the length of the recalculation string. So it's the same efficiency and pure C code, but it is easier to write, it is easier to maintain and easier to understand. If you still can't make it possible to find what happen, please see the source code of CString, strcore.cpp, in the MFC / SRC subdirectory of your VC98 installation directory. Take a look at the Concatinplace method, which is called in all = operators. Ah! Is CSTRING really "efficient"? For example, if I created
CString Cat ("MEW!"); Then I didn't get an efficient, streamlined 5-byte size buffer (4 characters plus an end character), the system will assign me 64 bytes, and 59 bytes were wasted. If you think about it, please have to accept re-education. It may be a good thing for someone who may tell you at a place. Yes, this statement is indeed correct, but he ignores a very important aspect of the facts. If you have written embedded programs running under 16K EPROMS, you have reason to use it less, in this environment, it makes your program more robust. But write Windows programs on a 500MHz, 256MB machine, if you still do it, it will only run even worse than the "inefficient" code you think. for example. The size of the string is considered to be the primary factor affecting efficiency, making the string to improve efficiency as little as possible, and vice versa, which is the consistent idea. But this idea is not right, the consequence of the exact memory allocation is to reflect after the program is running for several hours. At that time, the plurality of programs will be filled with the memory, they are too small to do it. Anything, but they add your program's memory, increase the number of memory pages exchange, increase the number of page exchanges to the upper limit of the system to endure, the system will allocate more pages for your program until you The program takes up all available memory. It can be seen that although memory debris is a secondary factor in determining efficiency, it is precisely that these factors actually control the behavior of the system. In the end, it harms the system reliability, which is unacceptable. Remember, in Debug mode, memory is often precisely allocated, which is for better troubleshooting. Suppose your app usually requires a few months. For example, I often open VC , Word, PowerPoint, FrontPage, Outlook Express, Forté Agent, Internet Explorer, and other programs, and usually do not close them. I have worked for a few days in the evening, and I have to use PowerPoint for a few days (contrary. If you are unfortunate, you will experience the reliability of reliability; this program will collapse 4 ~ 6 times every day Every time because all spaces are used and fill all my exchange pages). Therefore, precise memory allocation is not desirable, it will endanger the system reliability and cause the application to crash. The multiples of quantum are allocated to the string, and the memory distributor can recover the used memory blocks, usually these recycled memory blocks can be reused by other CString objects so that they can guarantee minimal fragmentation. The function of the dispenser is enhanced, and the memory used by the application can be as small as possible, such a program can run a few weeks or months without problems. Exterior: Many years ago, when we wrote a interactive system in CMU, some research on memory distributors showed that it often produced many memory fragments. Jim Mitchell, now he works in Sun Microsystems, then he created a memory distributor, which retained a runtime statistics for a memory allocation condition, which is different from the technologies used at the mainstream distributor at the time. And more lead. When a memory block needs to be divided into a small value, he does not divide it, so it can avoid the memory fragmentation that is not too small to do.
In fact, he uses a floating pointer in the memory distributor. He believes that it is better to simply ignore the operation of the floating pointer with it for a long time to access the memory. (His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.) He's right. Never think that the so-called "optimization" is based on each line of code, in fact, in fact, high speed and save memory should be considered in an application's overall level. On the overall level of the software, only the smallest memory string allocation strategy may be the worst way. If you think that Optimization is what you work on every line of code, you should think about it: Optimization made in each line of code is rarely actually played. You can see another article on the optimization problem "Your Worst Enemy for Some Thought-provarking ideas". Remember, = operator is just a special case, if you write it as follows: cstring s = somecstring1 SomeCstring2 SomeCstring3 "," SomeCstring4; then each application can cause a new string created and once Copy operation.
to sum up
The above is some tips using CString. I will use it when I write a program every day. CString is not a category that is difficult to use, but the MFC does not obviously point these features, you need you to explore themselves and discover.