_A C++ PORTABLE STRING CLASS_ by William Hill Listing One // Class: MXString -- class for managing zero terminated C-style strings // Author: W Hill #ifndef MXSTRING_HPP #define MXSTRING_HPP #include #include #include enum BOOL { FALSE, TRUE }; typedef const char *CSTR; const int MAX_VARGS_BUFLEN = 1024; const int MXSTRING_LOCATENOTFOUND = -1; class MXString { // public: // orthodox canonical form // see "Advanced C++ Programming Styles and Idioms", James O. Coplien MXString(); MXString(const MXString&); // copy constructor MXString(const char *); MXString(unsigned int nSprintfSize); // resize buffer for sprintf() virtual ~MXString(); virtual MXString& operator=(const MXString&); // assignment operator virtual MXString& operator=(CSTR); virtual MXString operator+(const MXString&) const; virtual MXString& operator+=(const MXString&); // type conversion operator CSTR() const; // duplication // user is responsible for deleting the returned pointer // just like ANSI C strdup() function char *strDup() const; // substring member functions/operators char& operator[](unsigned int index); // remember BASIC? MXString left(unsigned int len) const; // return first 'len' characters MXString mid(unsigned int start, unsigned int len) const; // return 'len' characters from offset 'start' MXString right(unsigned int len) const; // return last 'len' characters // substring/character functions return MXSTRING_LOCATENOTFOUND for // error. offset is 0 based virtual int locate(const MXString&) const; virtual int locate(const char c) const; // comparison operators // '>' and '<' are used for alphabetical sorting operators virtual BOOL operator>(const MXString&) const; virtual BOOL operator>(CSTR) const; virtual BOOL operator>=(const MXString&) const; virtual BOOL operator>=(CSTR) const; virtual BOOL operator<(const MXString&) const; virtual BOOL operator<(CSTR) const; virtual BOOL operator<=(const MXString&) const; virtual BOOL operator<=(CSTR) const; virtual BOOL operator==(const MXString&) const; virtual BOOL operator==(CSTR) const; virtual BOOL operator!=(const MXString&) const; virtual BOOL operator!=(CSTR) const; virtual int cmp(const MXString&) const; // case conversion member functions virtual void toUpper(); // converts instance to uppercase virtual void toLower(); // converts instance to lowercase // check/toggle sensitivity setting for all MXStrings static void sensitivity(BOOL b); static BOOL sensitivity(); // insertion; deletion members functions MXString& insert(unsigned int start, MXString&); MXString& erase(unsigned int start, unsigned int len); // handy printf formatting-type function; MXString& sprintf(CSTR fmt, ...); // returns length of zero terminated string // not length of allocated buffer unsigned int length() const; // return TRUE if string is empty BOOL isEmpty() const; BOOL operator!() const; // fills string with single character void fill(unsigned int len, const char c = ' '); // private: static BOOL bSensitive; // compares/searches are case sensitive ? char *rep; int nSprintfBufSize; MXString ncpy(unsigned int start, unsigned int len) const; }; inline MXString::operator CSTR() const { return rep; } inline MXString MXString::left(unsigned int len) const { return ncpy(0, len); } inline MXString MXString::mid(unsigned int start, unsigned int len) const { return ncpy(start, len); } inline unsigned int MXString::length() const { return strlen(rep); } inline MXString MXString::right(unsigned int len) const { return ncpy(length() - len, len); } inline BOOL MXString::isEmpty() const { return (*rep == '\0') ? TRUE : FALSE; } inline BOOL MXString::operator !() const { return isEmpty(); } inline void MXString::sensitivity(BOOL b) { MXString::bSensitive = b; } inline BOOL MXString::sensitivity() { return MXString::bSensitive; } class ostream; ostream& operator<<(ostream& s, MXString& m); #endif // MXSTRING_HPP Listing Two #include "mxstring.hpp" BOOL MXString::bSensitive; #ifdef sunos int stricmp(const char *s1, const char *s2); #endif // portable stricmp() #ifdef sunos int stricmp(const char *s1, const char *s2) { while(toupper(*s1++) == toupper(*s2++)) if(*s1 == '\0' && *s2 == '\0') return 0; if(toupper(*s1) < toupper(*s2)) return -1; else return 1; } #endif // portable stricmp() MXString::MXString() { rep = new char[1]; assert(rep); rep[0] = '\0'; nSprintfBufSize = MAX_VARGS_BUFLEN; } MXString::MXString(unsigned int nSprintfSize) { rep = new char[1]; assert(rep); rep[0] = '\0'; nSprintfBufSize = (nSprintfSize > 0) ? nSprintfSize : MAX_VARGS_BUFLEN; } MXString::MXString(const MXString& s) { rep = new char[s.length() + 1]; assert(rep); strcpy(rep, s.rep); nSprintfBufSize = MAX_VARGS_BUFLEN; } MXString::MXString(const char *s) { rep = new char[strlen(s) + 1]; assert(rep); strcpy(rep, s); nSprintfBufSize = MAX_VARGS_BUFLEN; } MXString::~MXString() { delete[] rep; } // As for all operators and functions that require possible reassigning to the // *rep pointer, a test is first made to verify that the existing string // buffer is larger than the incoming string. If so, make a straightforward // copy. Buffer space is freed only if incoming string requires extra space. MXString& MXString::operator=(const MXString& s) { if(rep != s.rep) { if(s.length() > length()) { delete[] rep; rep = new char[s.length() + 1]; assert(rep); } strcpy(rep, s.rep); } return *this; } MXString& MXString::operator=(const char *s) { if(rep != s) { if(strlen(s) > length()) { delete[] rep; rep = new char[strlen(s) + 1]; assert(rep); } strcpy(rep, s); } return *this; } MXString MXString::operator+(const MXString& s) const { char *tmp = new char[length() + s.length() + 1]; assert(tmp); strcpy(tmp, rep); strcat(tmp, s.rep); MXString retval = tmp; delete[] tmp; return retval; } MXString& MXString::operator+=(const MXString& s) { *this = *this + s; return *this; } char *MXString::strDup() const { char *tmp = new char[length() + 1]; assert(tmp); strcpy(tmp, rep); return tmp; } MXString MXString::ncpy(unsigned int start, unsigned int len) const { if(start > (length() - 1)) { MXString emptyString; return emptyString; } if(len > strlen(&rep[start])) len = strlen(&rep[start]); char *tmp = new char[len + 1]; assert(tmp); strncpy(tmp, &rep[start], len); tmp[len] = '\0'; MXString retval = tmp; delete[] tmp; return retval; } char& MXString::operator[](unsigned int index) { // return '\0' if the index value is out of bounds if(index < 0 || index > length()) return rep[length()]; return rep[index]; } int MXString::locate(const MXString& s) const { char *p; int off; if(MXString::sensitivity()) { p = strstr(rep, s.rep); off = p ? (int)(p - rep) : MXSTRING_LOCATENOTFOUND; } else { MXString src = *this; src.toUpper(); MXString tmp(s); tmp.toUpper(); p = strstr(src.rep, tmp.rep); off = p ? (int)(p - src.rep) : MXSTRING_LOCATENOTFOUND; } return off; } int MXString::locate(const char c) const { char *p; int off; if(MXString::sensitivity()) { p = strchr(rep, c); off = p ? (int)(p - rep) : MXSTRING_LOCATENOTFOUND; } else { MXString src = *this; src.toUpper(); char tmp = toupper(c); p = strchr(src.rep, tmp); off = p ? (int)(p - src.rep) : MXSTRING_LOCATENOTFOUND; } return off; } BOOL MXString::operator>(const MXString& s) const { if(MXString::sensitivity()) return strcmp(rep, s.rep) > 0 ? TRUE : FALSE; else return stricmp(rep, s.rep) > 0 ? TRUE : FALSE; } BOOL MXString::operator>(CSTR s) const { MXString str = s; return (*this > str); } BOOL MXString::operator>=(const MXString& s) const { return (s < *this); } BOOL MXString::operator>=(CSTR s) const { MXString str = s; return (str < *this); } BOOL MXString::operator<(const MXString& s) const { if(MXString::sensitivity()) return strcmp(rep, s.rep) < 0 ? TRUE : FALSE; else return stricmp(rep, s.rep) < 0 ? TRUE : FALSE; } BOOL MXString::operator<(CSTR s) const { MXString str = s; return (*this < str); } BOOL MXString::operator<=(const MXString& s) const { return (s > *this); } BOOL MXString::operator<=(CSTR s) const { MXString str = s; return (str > *this); } BOOL MXString::operator==(const MXString& s) const { if(MXString::sensitivity()) return strcmp(rep, s.rep) == 0 ? TRUE : FALSE; else return stricmp(rep, s.rep) == 0 ? TRUE : FALSE; } BOOL MXString::operator==(CSTR s) const { MXString str = s; return (*this == str); } BOOL MXString::operator!=(const MXString& s) const { return (*this == s) ? FALSE : TRUE; } BOOL MXString::operator!=(CSTR s) const { return (*this == s) ? FALSE : TRUE; } int MXString::cmp(const MXString& s) const { if(MXString::sensitivity()) return strcmp(rep, s.rep); else return stricmp(rep, s.rep); } void MXString::toUpper() { for(unsigned int i = 0; i < length(); i++) rep[i] = toupper(rep[i]); } void MXString::toLower() { for(unsigned int i = 0; i < length(); i++) rep[i] = tolower(rep[i]); } MXString& MXString::insert(unsigned int start, MXString& s) { if(start < (length() + 1)) { MXString strStart = ncpy(0, start); MXString strEnd = ncpy(start, length() - start); *this = strStart + s + strEnd; } return *this; } MXString& MXString::erase(unsigned int start, unsigned int len) { if(start < (length() + 1) && len <= strlen(&rep[start])) { MXString strStart = ncpy(0, start); MXString strEnd = ncpy(start + len, length() - (start + len)); *this = strStart + strEnd; } return *this; } void MXString::fill(unsigned int len, const char c) { if(len > length()) { delete[] rep; rep = new char[len + 1]; assert(rep); } memset(rep, c, len); *(rep + len) = '\0'; } #include #include MXString& MXString::sprintf(const char *fmt, ...) { char *szBuf = new char[nSprintfBufSize]; assert(szBuf); va_list args; va_start(args, fmt); int val = ::vsprintf(szBuf, fmt, args); va_end(args); // if retval >= MAX_VARGS_BUFLEN then // we have written past the end of the buffer // memory is probably trashed; an exception should be thrown here assert(val < nSprintfBufSize); *this = szBuf; delete[] szBuf; return *this; } #include ostream& operator<<(ostream& s, MXString& m) { s << (CSTR)m; return s; } Listing Three #include #include #include "mxstring.hpp" int main(int argc, char *argv[]) { MXString str("Hello, world!"); cout << "instance [str] == " << str << "\n"; cout << "MXString instances are " << sizeof(MXString) << " bytes in size" << "\n"; cout << "instance [str] is " << sizeof(MXString) << " bytes in size" << "\n"; cout << "instance [str] contains string representation of " << str.length() << " bytes in length" << "\n\n"; MXString strUp = "STRING"; MXString strLow = "string"; cout << "strUp == " << strUp << "\t" << "strLow == " << strLow << "\n"; MXString::sensitivity(FALSE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " == " << strLow << " : " << (int)(strUp == strLow) << "\n"; MXString::sensitivity(TRUE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " == " << strLow << " : " << (int)(strUp == strLow) << "\n"; MXString::sensitivity(FALSE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " != " << strLow << " : " << (int)(strUp != strLow) << "\n"; MXString::sensitivity(TRUE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " != " << strLow << " : " << (int)(strUp != strLow) << "\n\n"; strUp = "UP"; strLow = "low"; cout << "strUp == " << strUp << "\t" << "strLow == " << strLow << "\n"; MXString::sensitivity(FALSE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " < " << strLow << " : " << (int)(strUp < strLow) << "\n"; MXString::sensitivity(TRUE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " < " << strLow << " : " << (int)(strUp < strLow) << "\n"; MXString::sensitivity(FALSE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " > " << strLow << " : " << (int)(strUp > strLow) << "\n"; MXString::sensitivity(TRUE); cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strUp << " > " << strLow << " : " << (int)(strUp > strLow) << "\n\n"; MXString::sensitivity(FALSE); MXString strSrc("This string is for searching inside"); MXString strLocate("SEARCH"); char chLocate = 'G'; int rc = strSrc.locate(strLocate); cout << "Using source string : " << strSrc << "\n"; cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strLocate << " subchain search result : " << rc << "\n"; rc = strSrc.locate(chLocate); cout << "Using source string : " << strSrc << "\n"; cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << chLocate << " subchain search result : " << rc << "\n\n"; MXString::sensitivity(TRUE); rc = strSrc.locate(strLocate); cout << "Using source string : " << strSrc << "\n"; cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << strLocate << " subchain search result : " << rc << "\n"; rc = strSrc.locate(chLocate); cout << "Using source string : " << strSrc << "\n"; cout << "Sensitivity == " << (int)MXString::sensitivity() << "\n"; cout << chLocate << " subchain search result : " << rc << "\n\n"; MXString strBegin = "Beginning "; MXString strMiddle = "Middle "; MXString strEnd = "End"; cout << "strBegin == " << strBegin << " strMiddle == " << strMiddle << " strEnd == " << strEnd << "\n"; MXString strCat; strCat = strBegin + strMiddle + strEnd; cout << "strCat == " << strCat << "\n"; cout << "strCat.left(9) == " << (MXString)strCat.left(9) << "\n"; cout << "strCat.mid(10, 6) == " << (MXString)strCat.mid(10, 6) << "\n"; cout << "strCat.right(3) == " << (MXString)strCat.right(3) << "\n\n"; strCat.erase(0, 10); cout << "strCat.erase(0, 10) == " << strCat << "\n"; strCat.insert(0, (MXString)"Start "); cout << "strCat.insert(0, \"Start\") == " << strCat << "\n\n"; MXString strBig; if(argc > 1) { ifstream fin(argv[1]); if(fin.good()) { int nCount = 0; char szBuf[1024]; while(fin.getline(szBuf, sizeof(szBuf))) { strBig.sprintf("Line [%05d] : %s", ++nCount, szBuf); cout << strBig << "\n"; } } cout << "\n"; } #if defined _CONSOLE || sunos strBig.fill(1000000, '#'); // this works under WinNT and SunOS cout << strBig << "\n\n"; #endif // _CONSOLE || sunos // write over string used at beginning of program str = "Goodbye, world!"; cout << str << endl; return 0; } Example 1: Retrieving entries using a binary search BOOL F2HCode::verify(const char *src) { const int nRecLen = 5; // length of an indivdual field FFCursor *cursHead = new FFCursor("head13.dat", FALSE, nRecLen); char szBuf[nRecLen + 1]; BOOL retval = FALSE; long nLow = 1; // record numbers start at 1 // return the number of records in the fixed format file: long nHigh = cursHead -> numRecs(); while(nLow <= nHigh) { long nMid = (nLow + nHigh) / 2; cursHead -> gotoRecord(nMid); cursHead -> getRecord(szBuf); szBuf[sizeof(szBuf) - 1] = '\0'; if(strcmp(src, szBuf) < 0) nHigh = nMid - 1; else if(strcmp(src, szBuf) > 0) nLow = nMid + 1; else { retval = TRUE; break; } } delete cursHead; return retval; } Example 2: Nonportable function call between UNIX and DOS // fixed-format field write function BOOL FField::put(char *src) { memmove(szBuf + nOffset, src, nLength); return TRUE; } Example 3: Using a string class to continuously replace substrings in a charcater buffer by another sequence of characters // replaces one substring by another for an entire string // char *s : source character buffer // const char *x : substring to find // const char *y : substring to replace x with // int len : string length of s void replaceXbyY(char *s, const char *x, const char *y, int len) { if(strcmp(x, y) == 0) return; MXString strSrc = s; MXString strX = x; MXString strY = y; int pos; // position returned by locate() function // if pos == MXSTRING_LOCATENOTFOUND, // the substring was not found // find the offset of strX in strSrc while((pos = strSrc.locate(strX)) != MXSTRING_LOCATENOTFOUND) { strSrc.erase(pos, strX.length()); // erase this copy of StrX strSrc.insert(pos, strY); // insert StrY in its place } strncpy(s, strSrc, len); }