• Welcome to Jose's Read Only Forum 2023.
 

ProgEx33 -- C++ Version Of String::Parse And String::ParseCount

Started by Frederick J. Harris, November 26, 2009, 05:19:57 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris


/*
 ProgEx33 -- C++ Version Of String::Parse
 
 I remember being really thrilled when I discovered PowerBASIC's ParseCount -
 Parse combination.  Its language elements like that that really help applic-
 ation programmers get things done.  And that is the sort of language element
 which C always lacked - C's strtok() function notwithstanding.  So when I
 built my C++ String Class its something I wanted to include for sure.  Before
 looking at a C++ String Class implementation, lets first look at how Parse
 works in PowerBASIC so we're all on the same page. Lets imagine we have my
 name to Parse with my first, middle and last names separated by commas...
 
 'First PowerBASIC Version
 #Compile Exe
 #Dim All

 Function PBMain() As Long
   Local strArr() As String
   Local strName As String
   Register i As Long
 
   strName="Frederick, John, Harris"
   Redim strArr(ParseCount(strName,",")-1)
   Parse strName, strArr(), ","
   Print " i            strArr(i)"
   Print "========================"
   For i=0 To UBound(strArr,1)
     Print i, LTrim$(strArr(i))
   Next i
   Erase strArr()
   Waitkey$

   PBMain=0
 End Function

 ' i            strArr(i)
 '========================
 ' 0            Frederick
 ' 1            John
 ' 2            Harris
 
 
 //Then C++ Version With My String Class
 #include <stdio.h>
 #include "Strings.h"

 int main(void)
 {
  String s1("Frederick, John, Harris");
  unsigned int iCount;
  String* pStr=NULL;  

  iCount=s1.ParseCount(',');
  pStr=new String[iCount];
  s1.Parse(pStr,',');
  printf("&pStr[i]\tpStr[i].lpStr()\t\tpStr[i].lpStr()\n");
  printf("=======================================================\n");
  for(unsigned int i=0; i<iCount; i++)
  {
      pStr[i].LTrim();
      printf("%u\t\t%u\t\t\t%s\n",(unsigned int)&pStr[i],(unsigned int)pStr[i].lpStr(),pStr[i].lpStr());
  }    
  delete [] pStr;
  getchar();  
   
  return 0;  
 }

 The PowerBASIC version comes out to 21 lines and the C++ version to 24.  
 The comparison is certainly interesting though.  The above C++ code isn't
 exactly like the compilable version below because in that version I put all
 the processing code in a procedure named DoProcessing() so that the
 destructors for all the various Strings would get called and print diagnostic
 output statements for your examination.  As I mentioned several Program
 Examples back, destructors for Strings created in main() are sometimes not
 called until after the console has been closed and main() has exited.  I
 wanted to avoid that.
 
 In the past several String Class Program Examples I've only added a new
 feature or two in each new example.  At that rate it will take me forever to
 get through all this.  So in this ProgEx I'm going to just provide my whole
 Ansi version of my String Class as it now stands.  However, I'll provide some
 derivation information on the development of Parse because its so useful. The
 other members that emulate BASIC String functions such as Left, Right, Mid,
 InStr, Trim, etc., you can probably figure out from the source code or just
 use it in your programs.
 
 I set up my C++ Parse as a String Class member function that returns void and
 needs to have the token that will be parsed against and the block of memory
 that will manage the parsed Strings as parameters.  So the initial setup will
 involve a pointer to a String Object declaration such as this...
 
 String* pStr=NULL;
 
 This is somewhat analagous to the declaration of the strArr() variable in the
 PowerBASIC program...
 
 Local strArr() As String
 
 Now that C++ pointer to String object declaration will need to point to memory
 where some as yet undetermined number of String objects will lie.  The first
 order of business then is to determine the number of Strings in the main
 String that will need to be parsed.  I have a ParseCount member in my String
 Class that returns that number.  Its implementation is rather trieval in that
 it simply runs through a String object with a char* counting delimiters.  The
 number of Strings will be the number of delimiters counted plus one.  Its use
 is as follows...
 
 String s1("Frederick, John, Harris");
 
 iCount=s1.ParseCount(',');
 pStr=new String[iCount];
 
 Since there are three Strings in the main String we'll need to allocate room
 for three Strings with C++'s new operator.  These would be referenced then as
 
 pStr[0], pStr[1], and pStr[2]
 
 The entire block of memory allocated here will be 24 bytes because each String
 comprises 8 bytes; 4 bytes for private member pStrBuffer and 4 more for
 private member iAllowableCharacterCount.  For sure the parsed Strings don't go
 here.  What goes here is the String object 'overhead' that manages each String
 once it has been parsed.
 
 At this point I believe my narrative should jump to the output from running
 the program.  The first String Constructor call occurs when String s1 is
 declared and initialized simultaneously with a char string...
 
 String s1("Frederick, John, Harris");
 
 Following that is the call to ParseCount and the return of 3.  Then we have
 three more Uninitialized Constructor calls generated by this statement...
 
 pStr=new String[iCount];
 
 If you look at the value of the 'this' pointer you'll see it starts at 4009100
 and increments up to 4009116 in 8 byte increments for the reasons just
 described above.  You can think of this memory as the machinery that manages
 the Strings.  The Strings themselves will be located at the numbers held in
 the pStrBuffer members, and as you can see that memory is scattered about a
 good bit.  When we finally make the call to the Parse member...
 
 s1.Parse(pStr,',');
 
 ...our output jumps inside that function reproduced here without the printf
 statements...
 
 
 void String::Parse(String* pStr, char delimiter)
 {
  unsigned int i=0;
  char* pBuffer=0;
  char* c;
  char* p;

  pBuffer=new char[this->LenStr()+1];
  if(pBuffer)
  {
     p=pBuffer;
     c=this->pStrBuffer;
     while(*c)
     {
        if(*c==delimiter)   //We've hit a delimiter and we know that up to this
        {                   //point a null was copied after each non-delimiter
           pStr[i]=pBuffer; //char.  So we should have a null terminated String
           p=pBuffer;       //at pBuffer that can be directly assigned to one
           i++;             //of the pStr[i] Strings passed in.  Of course i is
        }                   //incremented after each assignment so we're in
        else                //high cotton.
        {
           *p=*c;  //What's pointed at by c isn't a delimiter so copy the char
           p++;    //p=pBuffer and then increment p, i.e., p++.  Then store a
           *p=0;   //null byte at p which is now directly after the last char.
        }
        c++;
     }
     pStr[i]=pBuffer;
     delete [] pBuffer;
  }
 }
 
 The 1st thing my Parse does is allocate a temporary local buffer to slop
 around in.  I make it as big as the original String to be parsed.  Then
 I simply start moving through the String to be parsed pointed to by pStrBuffer
 one byte at a time using a while loop construct.  If the byte pointed at by c
 ( *c ) is not a delimiter, I simply copy that byte to my slop buffer pBuffer.
 Then I increment p and put a null byte after the last character copied because
 the next byte at c could very well be a delimiter, in which case the string
 in pBuffer up to the null will have to be assigned to one of the pStr[i]
 passed in.  
 
 So the loop just keeps doing that, i.e., copying bytes to pBuffer and placing
 a null after each byte, until a delimiter is 'hit'.  When the delimiter is
 hit we're in the upper part of the if and the String accumulated in pBuffer
 is simply assigned to the correct pStr[i] parameter as determined by the i
 counter variable which itself gets incremented after each assignment.
 Interestingly, you can see how the pStr[i]=pBuffer assignment triggered the
 operator= member.  Its all there in the output from within Parse. You can see
 where each of the three Strings were parsed out, and you can see the results
 and all the implicated addresses back in DoProcessing.  Note finally how
 the destructors on all the various Strings were automatically called when
 DoProcessing() terminated.
*/

#include <stdio.h>
#include "Strings.h"

void DoProcessing(void)
{
String s1("Frederick, John, Harris");
unsigned int iCount;
String* pStr=NULL;  

iCount=s1.ParseCount(',');
printf("iCount = %u\n\n", iCount);
pStr=new String[iCount];
printf("pStr   = %u\n\n", (unsigned int)pStr);
s1.Parse(pStr,',');
printf("&pStr[i]\tpStr[i].lpStr()\t\tpStr[i].lpStr()\n");
printf("=======================================================\n");
for(unsigned int i=0; i<iCount; i++)
{
    pStr[i].LTrim();
    printf
    (
     "%u\t\t%u\t\t\t%s\n",
     (unsigned int)&pStr[i],(unsigned int)pStr[i].lpStr(),pStr[i].lpStr()
    );
}    
delete [] pStr;
return;      
}    

int main(void)
{
DoProcessing();
getchar();  
   
return 0;  
}


/*
Entering String(const char* pStr)  //Constructor: Initializes with char*
 this = 2293552
 pStr = Frederick, John, Harris
 pStrBuffer = 4009056  Frederick, John, Harris
Leaving String(const char* pStr)

iCount = 3

Entering String()  Uninitialized Constructor
 this = 4009100
 pStrBuffer = 4008952
Leaving Uninitialized Constructor

Entering String()  Uninitialized Constructor
 this = 4009108
 pStrBuffer = 4009136
Leaving Uninitialized Constructor

Entering String()  Uninitialized Constructor
 this = 4009116
 pStrBuffer = 4009152
Leaving Uninitialized Constructor

pStr   = 4009100

Entering Parse(String* pStr, char delimiter)
 pBuffer = 4009168
 Entering String::operator=(const char* pStr)
   pStr = 4009200      Frederick
 Leaving String::operator=(const char* pStr)

 Assigned pStr[0] In Parse()
 pStr[0]  = Frederick
 pStr[0]  = 4009200
 &pStr[i] = 4009100

 Entering String::operator=(const char* pStr)
   pStr = 4009136       John
 Leaving String::operator=(const char* pStr)

 Assigned pStr[1] In Parse()
 pStr[1]  =  John
 pStr[1]  = 4009136
 &pStr[i] = 4009108

 Entering String::operator=(const char* pStr)
   pStr = 4009224       Harris
 Leaving String::operator=(const char* pStr)

 Assigned pStr[2] In Parse()
 pStr[2]  =  Harris
 pStr[2]  = 4009224
 &pStr[i] = 4009116
Leaving Parse(String* pStr, char delimiter)

&pStr[i]        pStr[i].lpStr()         pStr[i].lpStr()
=======================================================
4009100         4009200                 Frederick
4009108         4009136                 John
4009116         4009224                 Harris

Entering String Destructor!
 this       = 4009116
 pStrBuffer = 4009224  Harris
Leaving String Destructor!


Entering String Destructor!
 this       = 4009108
 pStrBuffer = 4009136  John
Leaving String Destructor!


Entering String Destructor!
 this       = 4009100
 pStrBuffer = 4009200  Frederick
Leaving String Destructor!


Entering String Destructor!
 this       = 2293552
 pStrBuffer = 4009056  Frederick, John, Harris
Leaving String Destructor!
*/


Frederick J. Harris


//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION    8

class String
{
public:
String();                                //Uninitialized Constructor
String(const char);                      //Constructor Initializes String With char
String(const char*);                     //Constructor Initializes String With char*
String(const String&);                   //Constructor Initializes String With Another String (Copy Constructor)
String(const int);                       //Constructor Initializes Buffer To Specific Size
String& operator=(const char);           //Assigns char To String
String& operator=(const char*);          //Assigns char* To String
String& operator=(const String&);        //Assigns one String to another (this one)
String& operator+(const char);           //For adding char to String
String& operator+(const char*);          //For adding null terminated char array to String
String& operator+(const String&);        //For adding one String to Another
bool operator==(const String);           //For comparing Strings
String Left(unsigned int);               //Returns String of iNum Left Most chars of this
String Right(unsigned int);              //Returns String of iNum Right Most chars of this
String Mid(unsigned int, unsigned int);  //Returns String consisting of number of chars from some offset
String Remove(const char*, bool);        //Returns A String With A Specified char* Removed
int InStr(const char);                   //Returns one based offset of a specific char in a String
int InStr(const char*, bool);            //Returns one based offset of a particular char pStr in a String
int InStr(const String&, bool);          //Returns one based offset of where a particular String is in another String
void LTrim();                            //Removesleading spaces/tabs
void RTrim();                            //Removes spaces/tabs from end
void Trim();                             //Removes both leading and trailing whitespace
unsigned int ParseCount(const char);     //Returns count of Strings delimited by a char passed as a parameter
void Parse(String*, char);               //Returns array of Strings in first parameter as delimited by 2nd char delimiter
String CStr(const int);                  //Converts String to integer
String CStr(const unsigned int);         //Converts String to unsigned int
String CStr(const short int);            //Converts String to 16 bit int
String CStr(const double);               //Converts String to double
int iVal();                              //Returns int value of a String
int LenStr(void);                        //Returns length of string
char* lpStr();                           //Returns address of pStrBuffer member variable
void Print(bool);                        //Outputs String to Console with or without CrLf
~String();                               //String Destructor

private:
char* pStrBuffer;
int   iAllowableCharacterCount;
};
#endif

Frederick J. Harris


//Strings.cpp
#include  <stdlib.h>
#include  <stdio.h>
#include  <string.h>
#include  "Strings.h"

String::String()    //Uninitialized Constructor
{
puts("Entering String()  Uninitialized Constructor");   
pStrBuffer=new char[MINIMUM_ALLOCATION];
printf("  this = %u\n",(unsigned int)this);
printf("  pStrBuffer = %u\n",(unsigned int)pStrBuffer);
pStrBuffer[0]='\0';
this->iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
puts("Leaving Uninitialized Constructor\n");   
}


String::String(const char ch)  //Constructor: Initializes with char
{
pStrBuffer=new char[MINIMUM_ALLOCATION];
pStrBuffer[0]=ch;
pStrBuffer[1]='\0';
iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const char* pStr)  //Constructor: Initializes with char*
{
int iLen,iNewSize;

puts("Entering String(const char* pStr)  //Constructor: Initializes with char*");
printf("  this = %u\n", (unsigned int)this);
printf("  pStr = %s\n",pStr);
iLen=strlen(pStr);
iNewSize=(iLen/16+1)*16;
pStrBuffer=new char[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
strcpy(pStrBuffer,pStr);
printf("  pStrBuffer = %u\t%s\n",(unsigned int)pStrBuffer,pStrBuffer);

puts("Leaving String(const char* pStr)\n");
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
int iLen,iNewSize;

puts("Entering String(const String& s) Copy Constructor");
iLen=strlen(s.pStrBuffer);
iNewSize=(iLen/16+1)*16;
this->pStrBuffer=new char[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
strcpy(this->pStrBuffer,s.pStrBuffer);
printf("  this       = %u\n",(unsigned int)this);
printf("  pStrBuffer = %u\n",(unsigned int)pStrBuffer);
puts("Leaving String(const String& s) Copy Constructor\n");
}


String::String(const int iSize)  //Constructor Creates String With Custom Sized
{                                //Buffer (rounded up to paragraph boundary)
int iNewSize;

iNewSize=(iSize/16+1)*16;
pStrBuffer=new char[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
}


String& String::operator=(const char ch)  //Overloaded operator = for assigning a character to a String
{
this->pStrBuffer[0]=ch;
this->pStrBuffer[1]='\0';

return *this;
}


String& String::operator=(const char* pStr)   //Constructor For If Pointer To Asciiz String Parameter
{
int iLen,iNewSize;

puts("  Entering String::operator=(const char* pStr)");
iLen=strlen(pStr);
if(iLen<this->iAllowableCharacterCount)
{
    strcpy(pStrBuffer,pStr);
    printf("    pStr = %u\t%s\n",(unsigned int)pStrBuffer,pStrBuffer);   
}   
else
{
    delete [] pStrBuffer;
    iNewSize=(iLen/16+1)*16;
    pStrBuffer=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pStrBuffer,pStr);
    printf("    pStr = %u\t%s\n",(unsigned int)pStrBuffer,pStrBuffer);
}
puts("  Leaving String::operator=(const char* pStr)\n");

return *this;
}


String& String::operator=(const String& strRight)  //Overloaded operator = for
{                                                  //assigning another String to
int iRightLen,iNewSize;                           //a String

if(this==&strRight)
    return *this;
iRightLen=strlen(strRight.pStrBuffer);
if(iRightLen < this->iAllowableCharacterCount)
    strcpy(pStrBuffer,strRight.pStrBuffer);
else
{
    iNewSize=(iRightLen/16+1)*16;
    delete [] this->pStrBuffer;
    this->pStrBuffer=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pStrBuffer,strRight.pStrBuffer);
}

return *this;
}



bool String::operator==(const String strCompare)
{
if(strcmp(this->pStrBuffer,strCompare.pStrBuffer)==0)  //strcmp
    return true;
else
    return false;
}


String& String::operator+(const char ch)      //Overloaded operator + (Puts char in String)
{
int iLen,iNewSize;
char* pNew;

iLen=strlen(this->pStrBuffer);
if(iLen<this->iAllowableCharacterCount)
{
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
}
else
{
    iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pNew,this->pStrBuffer);
    delete [] this->pStrBuffer;
    this->pStrBuffer=pNew;
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
}

return *this;
}


String& String::operator+(const char* pChar) //Overloaded operator + (Adds char literals
{                                             //or pointers to Asciiz Strings)
int iLen,iNewSize;
char* pNew;

iLen=strlen(this->pStrBuffer)+strlen(pChar);
if(iLen<this->iAllowableCharacterCount)
{
    if(this->pStrBuffer)
       strcat(this->pStrBuffer,pChar);
    else
       strcpy(this->pStrBuffer, pChar);
}     
else
{
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount = iNewSize-1;   
    if(this->pStrBuffer)
    { 
       strcpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       strcat(pNew,pChar);
    }
    else
       strcpy(pNew,pChar);
    this->pStrBuffer=pNew;
}

return *this;
}


String& String::operator+(const String& strRight)  //Overloaded operator + Adds
{                                                  //Another String to the left
int iLen,iNewSize;                                //operand
char* pNew;

iLen=strlen(this->pStrBuffer) + strlen(strRight.pStrBuffer);
if(iLen < this->iAllowableCharacterCount)
{
    if(this->pStrBuffer)
       strcat(this->pStrBuffer,strRight.pStrBuffer);
    else
       strcpy(this->pStrBuffer,strRight.pStrBuffer);
}
else
{
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;   
    if(this->pStrBuffer)
    {
       strcpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       strcat(pNew,strRight.pStrBuffer);
    }
    else
       strcpy(pNew,strRight.pStrBuffer);
    this->pStrBuffer=pNew;
}

return *this;
}


String String::Left(unsigned int iNum)
{
unsigned int iLen,i,iNewSize;
String sr;

iLen=strlen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new char[iNewSize];
    for(i=0;i<iNum;i++)
        sr.pStrBuffer[i]=this->pStrBuffer[i];
    sr.pStrBuffer[iNum]='\0';
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Remove(const char* pToRemove, bool blnCaseSensitive)
{
int i,j,iParamLen,iReturn=0;
bool blnFound=false;

if(*pToRemove==0)
    return true;
iParamLen=strlen(pToRemove);
i=0, j=0;
do
{
  if(pStrBuffer[i]==0)
     break;
  if(blnCaseSensitive)
     iReturn=strncmp(pStrBuffer+i,pToRemove,iParamLen);  //strncmp
  else
     iReturn=strnicmp(pStrBuffer+i,pToRemove,iParamLen); //_strnicmp
  if(iReturn!=0)
  {
     if(blnFound)
        pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
  else   //made a match
  {
     blnFound=true;
     i=i+iParamLen;
     pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
}while(1);
if(blnFound)
    pStrBuffer[i-iParamLen]='\0';
String sr=pStrBuffer;

return sr;
}


String String::Right(unsigned int iNum)  //Returns Right$(strMain,iNum)
{
unsigned int iLen,i,j,iNewSize;
String sr;

iLen=strlen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new char[iNewSize];
    j=0;
    for(i=iLen-iNum;i<=iLen;i++)
    {
        //printf(_T("%u\t%u\t%c\n"),i,j,pStrBuffer[i]);
        sr.pStrBuffer[j]=this->pStrBuffer[i];
        j++;
    }
    sr.pStrBuffer[iNum]='\0';
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Mid(unsigned int iStart, unsigned int iCount)
{
unsigned int iLen,i,j,iNewSize;
String sr;

iLen=strlen(this->pStrBuffer);
if(iStart && iStart<=iLen)
{
    if(iCount && iStart+iCount-1<=iLen)
    {
       iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
       sr. iAllowableCharacterCount=iNewSize-1;
       sr.pStrBuffer=new char[iNewSize];
       j=0;
       sr.pStrBuffer=new char[iNewSize];
       for(i=iStart-1;i<iStart+iCount-1;i++)
       {
           sr.pStrBuffer[j]=this->pStrBuffer[i];
           j++;
       }
       sr.pStrBuffer[iCount]='\0';
       return sr;
    }
    else
    {
       sr=*this;
       return sr;
    }
}
else
{
    sr=*this;
    return sr;
}
}


int String::InStr(const char ch)
{
int iLen,i;

iLen=strlen(this->pStrBuffer);
for(i=0;i<iLen;i++)
{
     if(this->pStrBuffer[i]==ch)
        return (i+1);
}

return 0;
}


int String::InStr(const char* pStr, bool blnCaseSensitive)
{
int i,iParamLen,iRange;

if(*pStr==0)
    return 0;
iParamLen=strlen(pStr);
iRange=strlen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(strncmp(pStrBuffer+i,pStr,iParamLen)==0)   //strncmp
              return i+1;
        }
        else
        {
           if(strnicmp(pStrBuffer+i,pStr,iParamLen)==0)  //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive)
{
int i,iParamLen,iRange,iLen;

iLen=strlen(s.pStrBuffer);
if(iLen==0)
    return 0;
iParamLen=iLen;
iRange=strlen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(strncmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0)  //strncmp
              return i+1;
        }
        else
        {
           if(strnicmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0) //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


void String::LTrim()
{
unsigned int i,iCt=0,iLenStr;

iLenStr=this->LenStr();
for(i=0;i<iLenStr;i++)
{
     if(pStrBuffer[i]==32||pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(i=iCt;i<=iLenStr;i++)
        pStrBuffer[i-iCt]=pStrBuffer[i];
}
}


void String::RTrim()
{
unsigned int iCt=0, iLenStr;

iLenStr=this->LenStr()-1;
for(unsigned int i=iLenStr; i>0; i--)
{
     if(this->pStrBuffer[i]==32 || this->pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
this->pStrBuffer[this->LenStr()-iCt]=0;
}


void String::Trim()
{
this->LTrim();
this->RTrim();
}


unsigned int String::ParseCount(const char c)  //returns one more than # of
{                                              //delimiters so it accurately
unsigned int iCtr=0;                          //reflects # of strings delimited
char* p;                                      //by delimiter.

p=this->pStrBuffer;
while(*p)
{
  if(*p==c)
     iCtr++;
  p++;
}

return ++iCtr;
}


void String::Parse(String* pStr, char delimiter)
{
unsigned int i=0;
char* pBuffer=0;
char* c;
char* p;

puts("Entering Parse(String* pStr, char delimiter)");
pBuffer=new char[this->LenStr()+1];
printf("  pBuffer = %u\n",(unsigned int)pBuffer);
if(pBuffer)
{
    p=pBuffer;
    c=this->pStrBuffer;
    while(*c)
    {
     if(*c==delimiter)
     {
        pStr[i]=pBuffer;
        printf("  Assigned pStr[%u] In Parse()\n",i);
        printf("  pStr[%u]  = %s\n",i,pStr[i].lpStr());
        printf("  pStr[%u]  = %u\n",i,(unsigned int)pStr[i].lpStr());
        printf("  &pStr[i] = %u\n\n",(unsigned int)&pStr[i]);
        p=pBuffer;
        i++;
     }
     else
     {
        *p=*c;
        p++;
        *p=0;
     }
     c++;
    }
    pStr[i]=pBuffer;
    printf("  Assigned pStr[%u] In Parse()\n",i);
    printf("  pStr[%u]  = %s\n",i,pStr[i].lpStr());
    printf("  pStr[%u]  = %u\n",i,(unsigned int)pStr[i].lpStr());
    printf("  &pStr[i] = %u\n",(unsigned int)&pStr[i]);
    delete [] pBuffer;
}
puts("Leaving Parse(String* pStr, char delimiter)\n");
}


int String::iVal()
{
return atoi(this->pStrBuffer);  //atoi
}


String String::CStr(const int iNum)
{
String sr;
sprintf(sr.pStrBuffer,"%d",iNum);
return sr;
}


String String::CStr(const unsigned int iNum)
{
String sr;
sprintf(sr.pStrBuffer,"%u",iNum);
return sr;
}


String String::CStr(const short int iNum)
{
String sr;
sprintf(sr.pStrBuffer,"%d",iNum);
return sr;
}


String String::CStr(const double dblNum)
{
String sr(32);
sprintf(sr.pStrBuffer,"%f",dblNum);
return sr;
}


int String::LenStr(void)
{
return strlen(this->pStrBuffer);
}


char* String::lpStr()
{
return pStrBuffer;
}


void String::Print(bool blnCrLf)
{
printf("%s",pStrBuffer);
if(blnCrLf)
    printf("\n");
}


String::~String()   //String Destructor
{
printf("\nEntering String Destructor!\n");
printf("  this       = %u\n", (unsigned int)this);
printf("  pStrBuffer = %u\t%s\n",(unsigned int)pStrBuffer,pStrBuffer); 
delete [] pStrBuffer;
pStrBuffer=0;
printf("Leaving String Destructor!\n\n");
}

Frederick J. Harris


//Case Sensitive Search For Substring Using .InStr()
#include <stdio.h>
#include "Strings.h"

int main(void)
{
String s1("Frederick John Harris");
int iLoc=0;

iLoc=s1.InStr("John", true);
if(iLoc)
    printf("The Sub String 'John' Is Contained In s1 And Begins At %d\n",iLoc);
else
    printf("The Sub String 'John' Is Not Contained In s1\n");
getchar();   
   
return 0;   
}


/*
Entering String(const char* pStr)  //Constructor: Initializes with char*
  this = 2293584
  pStr = Frederick John Harris
  pStrBuffer = 4072496  Frederick John Harris
Leaving String(const char* pStr)

The Sub String 'John' Is Contained In s1 And Begins At 11
*/

Frederick J. Harris


//Non-Case Sensitive Search For Substring Using .InStr()
#include <stdio.h>
#include "Strings.h"

int main(void)
{
String s1("Frederick John Harris");
int iLoc=0;

iLoc=s1.InStr("john", false);  //use false for 2nd parameter
if(iLoc)
    printf("The Sub String 'John' Is Contained In s1 And Begins At %d\n",iLoc);
else
    printf("The Sub String 'John' Is Not Contained In s1\n");
getchar();   
   
return 0;   
}

/*
The Sub String 'John' Is Contained In s1 And Begins At 11
*/

Frederick J. Harris


/*
  Here would be kind of a work example for me that shows a common use of mine
  for Left, Right, and Mid.  Our foresters put their 100% tally timber marking
  data collector files (I wrote the programs with PB DOS) in C:\Tallies\RawData.
  The files have a *.mrk (mark) extension.  My desktop PowerBASIC program reads
  the binary file and creates an Access database containing the data in...
 
  C:\Tallies\DBFiles\SaleName.mdb
 
  where SaleName.mdb would be something like 15200901.mdb for the 1st sale in
  our District 15 for 2009.  So you start with a data collector file name and
  location such as this...
 
  C:\\Tallies\\RawData\\15200901.mrk
 
  and need to parse that string and create an Access database such as this...
 
  C:\\Tallies\\DBFiles\\15200901.mdb
*/


#include <stdio.h>
#include "Strings.h"

int main(void)
{
String s1("C:\\Tallies\\RawData\\15200901.mrk");
String s2,s3;

s2=s1.Left(11);
s2.Print(true);
s3=s1.Mid(20,8);
s3.Print(true);
s2=s2+"DBFiles\\"+s3+".mdb";
s2.Print(true);
getchar();   
   
return 0;   
}

/*
C:\Tallies\
15200901
C:\Tallies\DBFiles\15200901.mdb
*/

Patrice Terrier

Frederick,

Do you have a UNICODE version of your string class?

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Frederick J. Harris

Quote
Do you have a UNICODE version of your string class?


Yes, why don't I just post it in your forum Patrice, rather than making you hunt for it through that big COM tutorial of mine on the grid control?  The Strings.h and Strings.cpp files are actually at Reply #42 here ...

http://www.jose.it-berater.org/smfforum/index.php?topic=4176.0

But I'll post it up in your forum.  That Example 33 tutorial is my old String Class that I really need to update, as I don't use that one anymore.  What happened there is that I was working hard on that String Processing Comparison I posted about a long time ago in the PowerBASIC Forums, where I was comparing C, C++, and PowerBASIC String processing speeds, and in the process of doing that I had to redo my string class because I found that my concatenation operation was much, much slower than the one in the C++ Standard Library String Class, and I wanted to improve it.  In that tutorial example there were only two member variables in my String Class, and I added a third which cached the String length, rather than continuously calling the strlen() function, which was killing my performance.