Thanks.
> Is there an ExtractStrings equivalent that will not skip
> 'sections' that contain NULL values?
Try using the Delimiter and DelimitedText properties of TStringList.
TStringList *Temp = new TStringList;
try
{
Temp->Delimiter = ':';
Temp->DelimitedText = "A:B:C::E";
Dest->AddStrings(Temp);
}
__finally {
delete Temp;
}
If that still does not suit yor needs, then you will just have to parse the
string manually to get what you need.
Gambit
// Edit1->Text = "A:B:C::E"
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ParseAnsiStringToStrings( Edit1->Text, ':', ListBox1->Items );
}
// Reverse operation
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Edit2->Text = StringsToAnsiString( ListBox1->Items, ':' );
}
void TForm1::ParseAnsiStringToStrings( AnsiString AString, char AChar,
TStrings *AStrings )
{
if ( AString.Length() == 0 )
return;
AString = AString + AChar;
AStrings->Clear();
char *niz = new char[ AString.Length() + 1 ];
char *pom = new char[ AString.Length() + 1 ];
strcpy( niz, AString.c_str() );
int prev = 0;
for ( int i=0; i<AString.Length(); i++ )
{
if ( ( niz[i] == AChar ) || ( i == AString.Length()-1 ) )
{
strncpy( pom, &niz[prev], i-prev );
pom[i-prev] = '\0';
if ( AnsiString( pom ).Length() > 0 ) // <--- if NULL do nothing
AStrings->Add( AnsiString( pom ) );
prev = i + 1;
}
}
delete [] pom;
delete [] niz;
}
AnsiString TForm1::StringsToAnsiString( TStrings *AStrings, char AChar )
{
AnsiString Delimited;
for ( int i=0; i<AStrings->Count; i++ )
Delimited += AStrings->Strings[i] + AChar;
Delimited.SetLength( Delimited.Length()-1 );
return( Delimited );
}
--
Best regards,
Vladimir Stefanovic
> void TForm1::ParseAnsiStringToStrings( AnsiString AString, char AChar,
> TStrings *AStrings )
> {
> AString = AString + AChar;
That was a good idea!
But for the rest too complex.
AStrings->Clear();
if ( AString.Length() == 0 )
return;
AString = AString + AChar; // that was a good idea!
int start = 1;
int pos = 0;
int length = AString.Length(); //only call the Length() function once
while ( ++ pos <= length )
{
if ( AString[pos] == AChar )
{
AStrings->Add ( AString.SubString ( start, pos-start ) );
start = pos + 1;
}
}
}
Hans.
> If you want to make the function yourself, you can do
> something like this:
This would be a bit more accurate, and more memory efficient:
int __fastcall SeparateStrings(const AnsiString &AString, char
ADelimiter, TStrings *AStrings)
{
int Result = 0;
if( (AString.Length() > 0) && (AStrings) )
{
AStrings->BeginUpdate();
try
{
char *ptr = const_cast<AnsiString&>(AString).c_str();
char *end = (ptr + AString.Length());
while( (*ptr <= ' ') && (ptr < end) )
++ptr;
char *start = ptr;
bool InQuote = false;
while( ptr < end )
{
if( (*ptr == ADelimiter) && (!InQuote) )
{
AStrings->Add(AnsiString(start, ptr-start));
++Result;
start = ++ptr;
}
else if( *ptr == '"' )
{
InQuote = !InQuote;
++ptr;
}
else
++ptr;
}
if( start < end )
{
AStrings->Add(AnsiString(start, end-start));
++Result;
}
}
__finally {
AStrings->EndUpdate();
}
}
return Result;
}
AnsiString __fastcall JoinStrings(TStrings *AStrings, char ADelimiter)
{
AnsiString Result;
if( AStrings )
{
int Count = AStrings->Count;
if( Count > 0 )
{
int Len = (Count-1);
AnsiString S;
for(int i = 0; i < Count; ++i)
{
S = AStrings->Strings[i];
if( (S.Pos(ADelimiter) != 0) && (S[1] != '"') )
S = AnsiQuotedStr(S, '"');
Len += S.Length();
}
Result.SetLength(L);
char *ptr = Result.c_str();
for(int i = 0; i < Count; ++i)
{
if( i != 0 )
*ptr++ = ADelimiter;
S = AStrings->Strings[i];
if( (S.Pos(ADelimiter) != 0) && (S[1] != '"') )
S = AnsiQuotedStr(S, '"');
if( S.Length() > 0 )
{
StrLCopy(ptr, S.c_str(), S.Length());
ptr += S.Length();
}
}
}
}
return Result;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ListBox1->Clear()
SeparateStrings(Edit1->Text, ':', ListBox1->Items);
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Edit2->Text = JoinStrings(ListBox1->Items, ':');
}
> if( AnsiString( pom ).Length() > 0 ) // <--- if NULL do nothing
You are doing the very thing that Brian wants to void. If you are not going
to store empty strings, then there is no point in writing a replacement for
ExtractStrings() since it already ignores empty strings.
Gambit
Thanks.
That's my bad understanding of english :)
>Try using the Delimiter and DelimitedText properties of TStringList.
>
> TStringList *Temp = new TStringList;
> try
> {
> Temp->Delimiter = ':';
> Temp->DelimitedText = "A:B:C::E";
> Dest->AddStrings(Temp);
> }
> __finally {
> delete Temp;
> }
OR more simply:
TStringList *Tmp = new TStringList;
Tmp->Text = StringReplace(originalString,
":",
"\r\n",
TReplaceFlags()<<rfReplaceAll);
Happy Voyage,
The Capt'n