CString Extension
There are innumerable MFC functions that need a CString object as an argument or return a CString object. Often, even if a function needs a char pointer, we still use a CString to hold the string. Although, the CString class has lots of features, it does lack a few very useful string manipulation functions. A case insensitive search funciton is one, a search and replace function is another. Fortunately, implementing these functionality is not very difficult. The CStringEx class given below derives from CString and adds a few useful functions. The CStringEx class does not add any member variables of its own, so that it can be freely interchanged with a CString object.
The constructors in the CStringEx class parallel the constructor in CString and in fact simply chains into the corresponding CString constructor. Some of the CStringEx functions use knowledge of the internal structure of the CString object so there is a small chance that these functions might break if the CString implementation changes.
The functions provided by CStringEx are quite easy to use and fairly simple to understand. The Insert() functions inserts a character or a sub-string within a string. The result is similar to inserting text in an edit control, the string is expanded to accommodate the sub-string. The Delete() function removes a segment from the string and shortens it. The Replace() function removes a sub-string and replaces it with another. Again, the string size is adjusted depending on the size of the sub-string that was removed and the size of the sub-string that was inserted.
The find family of functions, finds a sub-string in the forward or the reverse direction. The NoCase version of these functions are case insensitive. The FindReplace() and FindReplaceNoCase() functions searches for a sub-string and replaces the matching sub-string with another string. The GetField() and GetDelimitedField() functions find a token in the string. The table below exemplifies the uses of these functions.
String |
Function |
Result |
xyz |
Insert(1, a) |
xayz |
xyz |
Insert(1, "ab") |
xabyz |
xyz |
Insert(4, "ab") |
xyz ab |
Abcde |
Delete(2,2) |
Abe |
Abcde |
Replace(2,2,"xyz") |
Abxyze |
Abc |
Find(b) |
1 |
Abcabc |
Find(b, 2) |
4 |
AbcAbc |
FindNoCase("aBC", 1) |
3 |
AbcAbc |
FindReplace("Ab","Xy") |
XycXyc |
AbcAbc |
FindReplace("Ab","Xy", FALSE) |
XycAbc |
AbcAbc |
FindReplaceNoCase("ab","Xy") |
XycXyc |
AbcAbc |
FindReplaceNoCase("ab","Xy", FALSE) |
XycAbc |
AbcAbc |
ReverseFind("bc") |
4 |
AbcAbc |
ReverseFindNoCase("abc") |
3 |
AbcAbc |
ReverseFindNoCase("abc", 2) |
0 |
AbcAbc |
GetField( :, 0 ) |
AbcAbc |
AbcAbc |
GetField( :, 1 ) |
(Blank string) |
Abc:Def |
GetField( :, 0 ) |
Abc |
Abc:Def |
GetField( :, 1 ) |
Def |
Abc:Def |
GetField( :, 2 ) |
(Blank string) |
Name: Zafir |
GetField("Name: ", 1) |
Zafir |
(415) 555-7777 |
GetDelimitedField("(", ")", 0 ) |
415 |
////////////////////////////////////////////////////////////////////// // StringEx.h // #ifndef __STRINGEX_H_ #define __STRINGEX_H_ class CStringEx : public CString { public: CStringEx() : CString( ){}; CStringEx( const CString& stringSrc) : CString( stringSrc ){}; CStringEx( const CStringEx& stringSrc) : CString( stringSrc ){}; CStringEx( TCHAR ch, int nRepeat = 1 ) : CString( ch, nRepeat ){}; CStringEx( LPCTSTR lpch, int nLength ) : CString( lpch, nLength ){}; CStringEx( const unsigned char* psz ) : CString( psz ){}; CStringEx( LPCWSTR lpsz ) : CString( lpsz ){}; CStringEx( LPCSTR lpsz ) : CString( lpsz ){}; CStringEx& Insert(int pos, LPCTSTR s); CStringEx& Insert(int pos, TCHAR c); CStringEx& Delete(int pos, int len); CStringEx& Replace(int pos, int len, LPCTSTR s); int Find( TCHAR ch, int startpos = 0 ) const; int Find( LPCTSTR lpszSub, int startpos = 0 ) const; int FindNoCase( TCHAR ch, int startpos = 0 ) const; int FindNoCase( LPCTSTR lpszSub, int startpos = 0 ) const; int FindReplace( LPCTSTR lpszSub, LPCTSTR lpszReplaceWith, BOOL bGlobal = TRUE ); int FindReplaceNoCase( LPCTSTR lpszSub, LPCTSTR lpszReplaceWith, BOOL bGlobal = TRUE ); int ReverseFind( TCHAR ch ) const{ return CString::ReverseFind(ch);}; int ReverseFind( LPCTSTR lpszSub, int startpos = -1 ) const; int ReverseFindNoCase( TCHAR ch, int startpos = -1 ) const; int ReverseFindNoCase( LPCTSTR lpszSub, int startpos = -1 ) const; CStringEx GetField( LPCTSTR delim, int fieldnum); CStringEx GetField( TCHAR delim, int fieldnum); int GetFieldCount( LPCTSTR delim ); int GetFieldCount( TCHAR delim ); CStringEx GetDelimitedField( LPCTSTR delimStart, LPCTSTR delimEnd, int fieldnum = 0); }; #endif ///////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // StringEx.cpp // #include "stdafx.h" #include "StringEx.h" // Insert - Inserts a sub string into the string // Returns - Reference to the same string object // pos - Position to insert at. Extends the string with spaces if needed // s - Sub string to insert CStringEx& CStringEx::Insert(int pos, LPCTSTR s) { int len = lstrlen(s); if ( len == 0 ) return *this; int oldlen = GetLength(); int newlen = oldlen + len; LPTSTR str; if ( pos >= oldlen ) { // insert after end of string newlen += pos - oldlen ; str = GetBuffer( newlen ); _tcsnset( str+oldlen, _T(' '), pos-oldlen ); _tcsncpy( str+pos, s, len ); } else { // normal insert str = GetBuffer( newlen ); memmove( str+pos+len, str+pos, sizeof(_T(' ')) *(oldlen-pos) ); _tcsncpy( str+pos, s, len ); } ReleaseBuffer( newlen ); return *this; } // Insert - Inserts a character into the string // Returns - Reference to the same string object // pos - Position to insert at. Extends the string with spaces if needed // c - Char to insert CStringEx& CStringEx::Insert(int pos, TCHAR c) { TCHAR buf[2]; buf[0] = c; buf[1] = _T('\0'); return Insert( pos, buf ); } // Delete - Deletes a segment of the string and resizes it // Returns - Reference to the same string object // pos - Position of the string segment to remove // len - Number of characters to remove CStringEx& CStringEx::Delete(int pos, int len) { int strLen = GetLength(); if( pos >= strLen) return *this; if(len < 0 ||len > strLen - pos) len = strLen - pos; LPTSTR str = GetBuffer( strLen ); memmove(str+pos, str+pos+len, sizeof(_T(' ')) *(strLen-pos)); ReleaseBuffer( strLen - len ); return *this; } // Replace - Replaces a substring with another // Returns - Reference to the same string object // pos - Position of the substring // len - Length of substring to be replaced // s - New substring CStringEx& CStringEx::Replace(int pos, int len, LPCTSTR s) { Delete(pos, len); Insert(pos, s); return *this; } // Find - Finds the position of a character in a string // Returns - A zero-based index // ch - Character to look for // startpos - Position to start looking from int CStringEx::Find( TCHAR ch, int startpos /*= 0*/ ) const { // find first single character LPTSTR lpsz = _tcschr(m_pchData + startpos, (_TUCHAR)ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // Find - Finds the position of a substring in a string // Returns - A zero-based index // lpszSub - Substring to look for // startpos - Position to start looking from int CStringEx::Find( LPCTSTR lpszSub, int startpos /*= 0*/ ) const { ASSERT(AfxIsValidString(lpszSub, FALSE)); // find first matching substring LPTSTR lpsz = _tcsstr(m_pchData+startpos, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // FindNoCase - Case insensitive find // Returns - A zero-based index // ch - Char to search for // startpos - Position to start looking from int CStringEx::FindNoCase( TCHAR ch, int startpos /*= 0*/ ) const { unsigned int locase = Find( tolower( ch ), startpos ); unsigned int upcase = Find( toupper( ch ), startpos ); return locase < upcase ? locase : upcase; } // FindNoCase - Case insensitive find // Returns - A zero-based index // lpszSub - Substring to search for // startpos - Position to start looking from int CStringEx::FindNoCase( LPCTSTR lpszSub, int startpos /*= 0*/ ) const { CStringEx sLowerThis = *this; sLowerThis.MakeLower(); CStringEx sLowerSub = lpszSub; sLowerSub.MakeLower(); return sLowerThis.Find( sLowerSub, startpos ); } // FindReplace - Find a substring and replace with another // Returns - Number of instances replaced // lpszSub - Substring to look for // lpszReplaceWith - Substring to replace with // bGlobal - Flag to indicate whether all occurances // should be replaced int CStringEx::FindReplace( LPCTSTR lpszSub, LPCTSTR lpszReplaceWith, BOOL bGlobal /*= TRUE*/ ) { int iReplaced = 0; // find first matching substring LPTSTR lpsz; int pos = 0; int lenSub = lstrlen( lpszSub ); int lenReplaceWith = lstrlen( lpszReplaceWith ); while( (lpsz = _tcsstr(m_pchData + pos, lpszSub)) != NULL ) { pos = (int)(lpsz - m_pchData); Replace( pos, lenSub, lpszReplaceWith ); pos += lenReplaceWith; iReplaced++; if( !bGlobal ) break; } return iReplaced; } // FindReplaceNoCase - Find a substring and replace with another // Does case insensitive search // Returns - Number of instances replaced // lpszSub - Substring to look for // lpszReplaceWith - Substring to replace with // bGlobal - Flag to indicate whether all occurances // should be replaced int CStringEx::FindReplaceNoCase( LPCTSTR lpszSub, LPCTSTR lpszReplaceWith, BOOL bGlobal /*= TRUE*/ ) { CStringEx sLowerThis = *this; sLowerThis.MakeLower(); CStringEx sLowerSub = lpszSub; sLowerSub.MakeLower(); int iReplaced = 0; // find first matching substring LPTSTR lpsz; int pos = 0, offset = 0; int lenSub = lstrlen( lpszSub ); int lenReplaceWith = lstrlen( lpszReplaceWith ); while( (lpsz = _tcsstr(sLowerThis.m_pchData + pos, sLowerSub.m_pchData)) != NULL ) { pos = (int)(lpsz - sLowerThis.m_pchData); Replace( pos+offset, lenSub, lpszReplaceWith ); offset += lenReplaceWith - lenSub; pos += lenSub; iReplaced++; if( !bGlobal ) break; } return iReplaced; } // ReverseFind - Searches for the last match of a substring // Returns - A zero-based index // lpszSub - Substring to search for // startpos - Position to start looking from, in reverse dir int CStringEx::ReverseFind( LPCTSTR lpszSub, int startpos /*= -1*/ ) const { int lenSub = lstrlen( lpszSub ); int len = lstrlen( m_pchData ); if(0 < lenSub && 0 < len) { if( startpos == -1 || startpos >= len ) startpos = len - 1; for ( LPTSTR lpszReverse = m_pchData + startpos ; lpszReverse != m_pchData ; --lpszReverse) if (_tcsncmp(lpszSub, lpszReverse, lenSub ) == 0) return (lpszReverse - m_pchData); } return -1; } // ReverseFindNoCase - Searches for the last match of a substring // Search is case insensitive // Returns - A zero-based index // lpszSub - Character to search for // startpos - Position to start looking from, in reverse dir int CStringEx::ReverseFindNoCase(TCHAR ch, int startpos /*= -1*/ ) const { TCHAR a[2]; a[0] = ch; a[1] = 0; return ReverseFindNoCase( a, startpos ); } // ReverseFindNoCase - Searches for the last match of a substring // Search is case insensitive // Returns - A zero-based index // lpszSub - Substring to search for // startpos - Position to start looking from, in reverse dir int CStringEx::ReverseFindNoCase( LPCTSTR lpszSub, int startpos /*= -1*/ ) const { //LPTSTR lpszEnd = m_pchData + lstrlen int lenSub = lstrlen( lpszSub ); int len = lstrlen( m_pchData ); if(0 < lenSub && 0 < len) { if( startpos == -1 || startpos >= len ) startpos = len - 1; for ( LPTSTR lpszReverse = m_pchData + startpos ; lpszReverse >= m_pchData ; --lpszReverse) if (_tcsnicmp(lpszSub, lpszReverse, lenSub ) == 0) return (lpszReverse - m_pchData); } return -1; } // GetField - Gets a delimited field // Returns - A CStringEx object that contains a copy of // the specified field // delim - The delimiter string // fieldnum - The field index - zero is the first // NOTE - Returns the whole string for field zero // if delim not found // Returns empty string if # of delim found // is less than fieldnum CStringEx CStringEx::GetField( LPCTSTR delim, int fieldnum) { LPTSTR lpsz, lpszRemainder = m_pchData, lpszret; int retlen, lenDelim = lstrlen( delim ); while( fieldnum-- >= 0 ) { lpszret = lpszRemainder; lpsz = _tcsstr(lpszRemainder, delim); if( lpsz ) { // We did find the delim retlen = lpsz - lpszRemainder; lpszRemainder = lpsz + lenDelim; } else { retlen = lstrlen( lpszRemainder ); lpszRemainder += retlen; // change to empty string } } return Mid( lpszret - m_pchData, retlen ); } // GetField - Gets a delimited field // Returns - A CStringEx object that contains a copy of // the specified field // delim - The delimiter char // fieldnum - The field index - zero is the first // NOTE - Returns the whole string for field zero // if delim not found // Returns empty string if # of delim found // is less than fieldnum CStringEx CStringEx::GetField( TCHAR delim, int fieldnum) { LPTSTR lpsz, lpszRemainder = m_pchData, lpszret; int retlen; while( fieldnum-- >= 0 ) { lpszret = lpszRemainder; lpsz = _tcschr(lpszRemainder, (_TUCHAR)delim); if( lpsz ) { // We did find the delim retlen = lpsz - lpszRemainder; lpszRemainder = lpsz + 1; } else { retlen = lstrlen( lpszRemainder ); lpszRemainder += retlen; // change to empty string } } return Mid( lpszret - m_pchData, retlen ); } // GetFieldCount - Get number of fields in a string // Returns - The number of fields // Is always greater than zero // delim - The delimiter character int CStringEx::GetFieldCount( TCHAR delim ) { TCHAR a[2]; a[0] = delim; a[1] = 0; return GetFieldCount( a ); } // GetFieldCount - Get number of fields in a string // Returns - The number of fields // Is always greater than zero // delim - The delimiter string int CStringEx::GetFieldCount( LPCTSTR delim ) { LPTSTR lpsz, lpszRemainder = m_pchData; int lenDelim = lstrlen( delim ); int iCount = 1; while( (lpsz = _tcsstr(lpszRemainder, delim)) != NULL ) { lpszRemainder = lpsz + lenDelim; iCount++; } return iCount; } // GetDelimitedField - Finds a field delimited on both ends // Returns - A CStringEx object that contains a copy of // the specified field // delimStart - Delimiter at the start of the field // delimEnd - Delimiter at the end of the field CStringEx CStringEx::GetDelimitedField( LPCTSTR delimStart, LPCTSTR delimEnd, int fieldnum /*= 0*/) { LPTSTR lpsz, lpszEnd, lpszRet, lpszRemainder = m_pchData ; int lenDelimStart = lstrlen( delimStart ), lenDelimEnd = lstrlen( delimEnd ); while( fieldnum-- >= 0 ) { lpsz = _tcsstr(lpszRemainder, delimStart); if( lpsz ) { // We did find the Start delim lpszRet = lpszRemainder = lpsz + lenDelimStart; lpszEnd = _tcsstr(lpszRemainder, delimEnd); if( lpszEnd == NULL ) return""; lpszRemainder = lpsz + lenDelimEnd; } else return ""; } return Mid( lpszRet - m_pchData, lpszEnd - lpszRet ); }
Comments
Very Useful but...
Posted by rajith_cherian on 04/07/2007 03:26amREAL fix for VS7/VS8
Posted by llllskywalker on 01/14/2007 07:26pmI don't like the idea of using #define to fool the compiler -- it seems to me like a hack that could easily bite you in the a$$ later on down the road. i'm using VS8. so to fix CStringEx, i replaced all 22 instances of "m_pchData" with (LPTSTR)GetString(). there were three exceptions in the FindReplaceNoCase() function where i needed to replace something like sLowerThis.m_pchData with (LPTSTR)sLowerThis.GetString(). (same for sLowerSub) after i did that, my CStringEx class was happy once again ;-)
ReplyFix for MFC v7.xx not working for 7.1
Posted by LightAvatar on 08/03/2005 07:55amIt's not working for version 7.1.3088. They probably fixed the "fool the compiler" thing. ".. error C2248: cannot access private member declared in class .."
correct fix for VS7/VS8
Posted by llllskywalker on 01/14/2007 07:23pmFix for MFC v7.1
Posted by caswi on 01/16/2006 04:33amtry to replace "m_pchData" with "GetString()"
ReplyCode for MFC v7.xx
Posted by timber on 03/28/2005 05:44pmSteve Palmer
Posted by timber on 03/28/2005 11:20amVS7 error: "m_pchData undefined"
Posted by Legacy on 04/26/2003 12:00amOriginally posted by: Mykel
Hi,
the class compiles fine under Visual Studio 6. But now I'm using VS7 and I get the followring error:
"m_pchData undefined"
Don't know how to fix this. Does anybody have a clue?
Did they change the CString class?
Many thx in advance...
Mykel
Steve Palmer
Posted by timber on 03/28/2005 11:17amSteve Palmer
Posted by timber on 03/28/2005 11:14amProblem still not solved !!!
Posted by mYkel on 09/20/2004 08:55amRecently I got a few mails from coders that also have the problem with "m_pchData undefined" under VS7. I still don't have a solution or a workaround for the problem. I believe that some parts of the CStringEx class must be recoded cause the MFC base classes changed... but I really have no time at the moment. If anybody finds a solution please post it here!!! :-) Regards, mYkel
ReplyThanks!
Posted by Legacy on 06/28/2002 12:00amOriginally posted by: Gayatri
Thank You a million times...this article is a life saver!:)
Best Regards
ReplyGayatri.
Don't compile with devstudio 7
Posted by Legacy on 06/11/2002 12:00amOriginally posted by: Dieter Hammer
this worked fine for me. Now I want to recompile the application with VC7. But m_pchDATA ist undefined.
ReplyReverseFind error
Posted by Legacy on 04/17/2002 12:00amOriginally posted by: Mouse
ReverseFind doesnt work if the string your are trying to find is at the front of the string.
ReplyThanks
Posted by Legacy on 09/13/2001 12:00amOriginally posted by: d.e. bugger
The code plugged right into my large project and seems to work well so far. I'm using it primarily for the reverse find functionality.
In addition, I'd like to see something that provides "entire word" search (in reverse) for the standard CFindReplace dialog. Good job!
ReplyLoading, Please Wait ...