• Welcome to PowerBasic Museum 2020-A.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

String::Remove(const TCHAR* pStr)

Started by Frederick J. Harris, June 28, 2017, 06:21:18 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

String::Remove() is kind of interesting.  This is one that over the years I've gone back and forth on in terms of whether to take the existing object and alter it by removing something or other from it, or maintain the existing object 'as is' and return an altered object in a second string as a return value.  The String Class as I now have it does the later.  Here is an example of that...


// cl Test02.cpp Strings.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdio.h"
#include "Strings.h"

int main()
{
String s,s1;

s=L"  one ,  two  ,  three,  four ,  five    ,   six";
s.Print(true);
s1=s.Remove(L" ");
s1.Print(true);
getchar();

return 0;
}

#if 0

C:\Code\VStudio\VC++9\Silvah\Sil7>cl Test02.cpp Strings.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test02.cpp
Strings.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test02.exe
Test02.obj
Strings.obj
TCLib.lib
kernel32.lib
user32.lib

C:\Code\VStudio\VC++9\Silvah\Sil7>Test02
  one ,  two  ,  three,  four ,  five    ,   six
one,two,three,four,five,six

#endif


So the key to getting that to work is to declare a 2nd String to receive the returned String with the spaces removed.

Frederick J. Harris

Now, interestingly enough, or strangely enough, my String::Trim(), String::LTrim(), and String::RTrim() don't behave like that.  They alter the existing object.  Here is an example showing that...


// cl Test03.cpp Strings.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdio.h"
#include "Strings.h"

int main()
{
String* pStrs=NULL;
int iCnt=0;
String s;

s=L"  one ,  two  ,  three,  four ,  five    ,   six";
s.Print(true);
iCnt=s.ParseCount(L',');
pStrs=new String[iCnt];
s.Parse(pStrs,L',',iCnt);
for(size_t i=0; i<iCnt; i++)
     pStrs[i].Print(false);
printf("\n");
for(size_t i=0; i<iCnt; i++)
{
     pStrs[i].Trim();
     pStrs[i].Print(false);
}     
delete [] pStrs;   
getchar();

return 0;
}

#if 0


C:\Code\VStudio\VC++9\Silvah\Sil7>Test02
  one ,  two  ,  three,  four ,  five    ,   six
  one   two    three  four   five       six
onetwothreefourfivesix

#endif

James C. Fuller

Fred,
  Ok. Thanks Fred.
  Funny you should mention the trims. Those are the ones I modified so as to come in line with the ones I use and am used to in bc9.
I added a parameter so you could trim any other single char.
James


changed in Strings.h:
[code]
  void LTrim(int charToTrim=32);                                                  // Removes leading white space by modifying existing String
  void RTrim(int charToTrim=32);                                                  // Removes trailing white space by modifying existing String
  void Trim(int charToTrim=32);                                                   // Removes leading or trailing white space from existing String

changed in Strings.cpp

void String::LTrim(int charToTrim)
{
size_t iCt=0;

for(size_t i=0; i<this->iLen; i++)
{
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32||this->lpBuffer[i]==charToTrim)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(size_t i=iCt; i<=this->iLen; i++)
        this->lpBuffer[i-iCt]=this->lpBuffer[i];
}
this->iLen=this->iLen-iCt;
this->blnSucceeded=TRUE;
}

void String::RTrim(int charToTrim)
{
int iCt=0;

for(int i=this->iLen-1; i>0; i--)
{
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32||this->lpBuffer[i]==charToTrim )
        iCt++;
     else
        break;
}
this->lpBuffer[this->iLen-iCt]=0;
this->iLen=this->iLen-iCt;
this->blnSucceeded=TRUE;
}

void String::Trim(int charToTrim)
{
this->LTrim(charToTrim);
this->RTrim(charToTrim);
this->blnSucceeded=TRUE;
}



Frederick J. Harris


James C. Fuller

Fred,
  This seems to work. Do you see any problems reusing s1?
James


typedef String fstring;
int _tmain ()
{
    fstring  s1(_T("Zero,  One, Two  , Three ,  Four, Five   , Six, Seven, Eight , Nine, Ten,"));
    s1.Print( true);
    s1 = s1.Remove(_T(" "));
    s1.Print( true);
    // Remove Trailing comma
    s1.RTrim(44);
    s1.Print( true);
}


output:

Zero,  One, Two  , Three ,  Four, Five   , Six, Seven, Eight , Nine, Ten,
Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,
Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten

Patrice Terrier

Here is what i am using in my code for triming without using a dedicated string class...

long find_wchar(IN WCHAR* sChar, IN WCHAR* sItem) {
    long nRet = -1;
    long K = 0, nLen = lstrlen(sChar);
    for (K = 0; K < nLen; K++) {
        if (sChar[K] == sItem[0]) { nRet = K; }
    }
    return nRet;
}

void reverse(WCHAR str[], long length) {
    int start = 0;
    int end = length -1;
    while (start < end) {
        swap(*(str + start), *(str + end));
        start++;
        end--;
    }
}

WCHAR* RTRIM(IN WCHAR* sBuf, IN WCHAR* sChar) {
    long nLength = lstrlen(sBuf);
    long nLen = lstrlen(sChar);
    if ((nLength) && (nLen)) {
        while (nLength > 0) {
            nLength -= 1;
            if (find_wchar(sChar, &sBuf[nLength]) > -1) {
                sBuf[nLength] = L'\0';
            } else {
                break;
            }
        }
    }
    return sBuf;
}

WCHAR* LTRIM(IN WCHAR* sBuf, IN WCHAR* sChar) {
    reverse(sBuf, lstrlen(sBuf));
    sBuf = RTRIM(sBuf, sChar);
    reverse(sBuf, lstrlen(sBuf));
}

WCHAR* TRIM(IN WCHAR* sBuf, IN WCHAR* sChar) {
    return LTRIM(RTRIM(sBuf, sChar), sChar);
}

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

James C. Fuller

Patrice,
  Pretty cool.
I have all the [w]char routines built into bc9 but I prefer Fred's string class because I can do this without the overhead of the circular buffer used for returns of character strings in bc9.
James


fstring GetHello ()
{
    fstring  fsHello(_T("Hello There!"));
    return fsHello;
}

int _tmain ()
{
    fstring  fs = {0};
    fs = GetHello();
    fs.Print( true);
}

Frederick J. Harris

#7
I'll do some debug console output on that later today Jim just to make sure, but my guess is that it'll be OK.  If it isn't crashing anywhere then the only concern would be memory leaks, so that's what I'll be looking for.  Good idea, by the way.  I hadn't thought of that.

Originally when I first wrote my string class years ago I had it that the original object was modified, then in one of the apps where I was using the string class I ran into an issue where I kept needing the original unmodified string.  It was at that point it occurred to me I should perhaps modify the method to return a modified string, rather than altering the original string.  I suppose it could be argued either way.  Or do you think my original thought of modifying the original string is best?

That issue served as a lesson to me though in that in all my further work on the String Class I always gave a thought to whether I want to modify the original String (this) or leave it be and create/return the altered one.

James C. Fuller

Fred,
  bc9 has both function and statement string procedures for a couple of it's calls.

------------------------------------------------------------------------------
SubStr$ = Remove$(Main$,Match$)
Remove Match$ FROM Main$
------------------------------------------------------------------------------
SubStr$ = Replace$(Main$,Match$,Change$)
Replace Match$ WITH Change$ IN Main$
------------------------------------------------------------------------------


but all the rest do not modify the Main$ and return a new string.


James

Frederick J. Harris

I think I just did a fairly extensive job of testing for memory leaks and I'm not finding any.  Actually, the version where the return from the Remove is being assigned to itself yields the least amount of code overhead.  Here's the results of a sample run...


// Main.cpp
// cl Main.cpp Strings.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
// 4,608 Bytes VC15 (VStudio 2008); x64; UNICODE
#ifndef UNICODE
   #define   UNICODE
#endif
#ifndef _UNICODE
   #define   _UNICODE
#endif
#include <windows.h>
#include "stdio.h"
#include "Strings.h"

int main()
{
String s1(L"one  , two,     three,  four     ");  // 840
 
s1=s1.Remove(L" ");                               // 952
s1.Print(true);
getchar();

return 0;
}

/*
Entering String::String(const TCHAR* pStr) Constructor!
  this->lpBuffer = 1739840
Leaving String::String(const TCHAR* pStr) Constructor!

Entering String String::Remove(const TCHAR* pStr)
  this->lpBuffer   = 1739840
  Entering String::String(const size_t iSize, bool blnFillNulls)
    this->lpBuffer = 1739952
  Leaving String::String(const size_t iSize, bool blnFillNulls)
Leaving String String::Remove(const TCHAR* pStr)

Entering String& String::operator=(const String& strAnother)
  strAnother.lpBuffer = 1739952
  this->lpBuffer      = 1739840
  this->lpBuffer      = 1739840
Leaving String& String::operator=(const String& strAnother)

Entering String Destructor!
  this->lpBuffer = 1739952
  this->lpBuffer = one,two,three,four
Leaving String Destructor!

one,two,three,four

Entering String Destructor!
  this->lpBuffer = 1739840
  this->lpBuffer = one,two,three,four
Leaving String Destructor!

840  deleted
952  deleted
*/