Solved

PChar problem

Posted on 1998-07-21
12
839 Views
Last Modified: 2010-04-04
These PChars are driving me nuts...they never give a result you'd expect...very unpredictable. I have a string array with about 20 character strings of 255 chars each. I simply want to loop thru them in a 'for' loop, and tack all these character strings together into 1 Pchar variable. Like this:

For x:=1 to 20 do
begin
  hpchar:=nil;
  StrPCopy(hpchar, strarray[x]);
  StrCat(mainpchar, hpchar);
end;


(hpchar and mainpchar are declared as PChar variables)

..I've tried every darn variation you can think of...with StrAlloc, StrLCat, StrCat...nothing works right.
I can't even initialize my 'mainpchar' variable right...if I initialize it to nil and then try to tack something on to it, it gives a EAccessViolation error. Very frustrating, these PChar's.

Regards,
  Shawn Halfpenny
  drumme59@sprint.ca

P.S: How come I never receive email notification when I question is answered? I always have the little box checked for it?
0
Comment
Question by:aztec
  • 6
  • 3
  • 2
  • +1
12 Comments
 

Expert Comment

by:boabyte
ID: 1358365
Have you tried reading the string array (I assume an array of characters) into a string and then using StrPCopy. I use this a lot and it works fine?

Westy
0
 

Expert Comment

by:boabyte
ID: 1358366
ie in line before StrPCopy do

  MyString := StrArray[x] ;
  StrPCopy(hpchar,MyString) ;

This is a technique I use from the Delphi 1 days as sometime (unknown reason, maybe optimised away) you have to read a value into a local variable. Examples I have dound is from reading a Database value ie  using  DB1.fieldbyname('FIELD1').AsString rather than reading it into a variable. Anyway hope it helps.

Westy(boabyte)
0
 
LVL 4

Accepted Solution

by:
erajoj earned 50 total points
ID: 1358367
Just cheat like this, very effective (on D2?/D3/D4):

var
  iIndex: Integer;
  sTemp: string; // only available on the stack
begin
  sTemp := '';
  StrDispose( mainpchar ); // prevent heap leaks
  for iIndex := Low( strarray ) to High( strarray ) do AppendStr( sTemp, strarray[ iIndex ] ); // ordinary string operations
  mainpchar := StrAlloc( Length( sTemp ) + 1 ); // reserve space for zero terminator
  mainpchar := StrPCopy( sTemp ); // copy from stack to heap
end;

or (with ShortStrings, not as efficient):

var
  iIndex: Integer;
  hpchar: PChar; // temporary chararray pointer
begin
  hpchar := StrAlloc( ( High( strarray ) - Low( strarray ) + 1 ) * 255 + 1 ); // could be smarter, but I'm lazy
  StrDispose( mainpchar ); // prevent heap leaks
  for iIndex := Low( strarray ) to High( strarray ) do StrCat( hpchar, PChar( strarray[ iIndex ] ) ); // PChar concatenisation instead
  mainpchar := StrAlloc( StrLen( hpchar ) + 1 ); // reserve space for zero terminator
  mainpchar := StrCopy( mainpchar, hpchar ); // copy from stack to heap
  StrDispose( hpchar ); // not to be forgotten, pointer will be garbagecollected and leak heap
end;

Could be typos above.

/// John
0
 

Author Comment

by:aztec
ID: 1358368
No, you guys aren't getting it. I tried to keep my explantion simple before but i might as well give you all the details now:
I'm using a sort utility which accepts multiple input files passed through a PChar variable (ie. 'infile1.txt+infile2.txt+infile3.txt...'). Also, a person using my application could potentially select THOUSANDS of input files. Now, since the StrAlloc function can only allocate a length of 65536 (..Cardinal variable..according to D3 documentation), this won't do as the length of 65536 *could* possibly be exceeded. I thought PChars were supposed to be virtually limitless anyway...I'm confused?
   Anyway, in my app, I allow the user to select his input files...I save these file names in a ListBox as ListBox string Items.

Here's basically the code I got, but it doesn't work right. All I want is all these filename strings to be tacked together - one after another (with a '+' sign in between them), into one big long PChar variable..

infileP:=nil;    {initialize..}
for i:=0 to (ListBox5.items.count - 1) do
begin
  if m <> 0 then StrCat(infileP, '+');
  hinfile:=stralloc(1000);  
  StrPCopy(hinfile, ListBox5.items[i]);
  StrCat(infileP, hinfile);
  strdispose(hinfile);
end;
0
 
LVL 4

Expert Comment

by:erajoj
ID: 1358369
Did you read my answer?
1. StrAlloc can allocate 2 GB long PChars. Read 2 lines further down in the help file.
2. Use AppendStr with Pascal-style strings. Then you will get dynamic allocation and a radically faster code on large counts of strings, see my first example.

var
  iIndex: Integer;
  sTemp: string; // only available on the stack
begin
  if ( Listbox5.Items.Count = 0 ) then Exit;
  StrDispose( infileP ); // prevent heap leaks

  sTemp := Listbox5.Items[ 0 ];
  for iIndex := 0 to Listbox5.Items.Count - 1 do AppendStr( sTemp, '+' + Listbox5.Items[ iIndex ] ); // ordinary string operations
  infileP := StrAlloc( Length( sTemp ) + 1 ); // reserve space for zero terminator, this is essential even before StrCat!
  infileP := StrPCopy( sTemp ); // copy from stack to heap
end;

/// John
0
 
LVL 3

Expert Comment

by:vladika
ID: 1358370
Why you use StrAlloc, StrPCopy etc.

Just
var infile: string; // not local variable

in the procedure

  infile := '';
  for I := 0 to ListBox5.Items.Count - 1 do
  begin
    if I <> 0 then infile := infile + '+';
    infile := infile + ListBox5.Items[I];
  end;

if you want use infile as null-terminated string
use PChar(infile) and all
 
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 4

Expert Comment

by:erajoj
ID: 1358371
My comment/answer was an example of how to use the functions, nothing else.
I supposed Shawn had his reasons for using PChars, probably for self-educational purposes, so therefor...
To be able to PChar-typed long strings is a fundament of OP9+, so everybody should already know that.

/// John
0
 

Author Comment

by:aztec
ID: 1358372
Hi John...
  Thanks for your answer. I always thought that strings could only be 255 characters in length? They can be bigger? That's why I've been trying to do all this conversion stuff to PChar.
  Does this mean I have to turn the 'Huge Strings' option on? I was told some time back to turn these off as they were causing major problems in migrating my app from Delphi 1 to Delphi 3.
 
   anyway, regarding your answer...a few questions:

sTemp := Listbox5.Items[ 0 ];
for iIndex := 0 to Listbox5.Items.Count - 1 do AppendStr( sTemp, '+' +Listbox5.Items[ iIndex ] ); // ordinary string operations

The 'AppendStr' statement generates this error for me :
  "Types of actual and formal var parameters must be identical".
  Any thoughts?

  So basically what you're proposing is to tack the whole thing together in a regular string variable, THEN convert that whole thing to a PChar. OK, so a string varibale is in fact NOT limited to 255 chars then?

Also in the 'For' statement, I assume you meant to start the 'For' loop with iIndex = 1...not 0 as you have.

Cheers
   Shawn Halfpenny
0
 
LVL 4

Expert Comment

by:erajoj
ID: 1358373
Hi again,
My NT-partition blew up a few days ago and I haven't gotten to reinstalling Delphi yet so the code was not tested at all.
I'm not sure why AppendStr doesn't work, but you can replace it with "sTemp := sTemp+...".
Always use huge strings in your 32-bit apps. The migration problems occur since there is no position 0 in the huge strings, but there is a #0 (zero terminator) put at the end of it.
All your remarks are correct.
If I get Delphi up and running today(CET), I will take a look at the AppendStr problem and get back to you.
By the way...there is seldom any need for PChar use in Delphi32 code at all. Most things can be handled with strings and simple type-conversions (as vladika wrote).

/// John
0
 
LVL 4

Expert Comment

by:erajoj
ID: 1358374
Hi again...
Delphi up and running...
I can't repeat your "Types of actual and formal var parameters must be identical"-error.
I tried different solutions using AppendStr, StrCat, Move...what did I discover?
The AppendStr method is about 3 times faster than the StrCat one.
I tried to concatenate 10000 300 char long strings, and on my machine it took about 180 seconds to do so.
I also tried to use Move. It was much, much, much (even more much) faster, but it is rather complicated to implement.
Here's my xtremely optimized solution ( ~400 times faster on 5000 strings (~150 ms))(this code works!):

function TForm1.AssembleString( list: TStrings ): string;
var
  iIndex, iLen, iCount: Integer;
  szBuf: PChar;
  sTemp: string;
begin
  {$B-} // make sure you compile w/o complete bool eval
  if ( ( not Assigned( list ) ) and ( ListBox1.Items.Count = 0 ) ) then begin // no need to continue...
    Result := '';
    Exit;
  end;
  iCount := list.Count;
  iLen   := 0;

  for iIndex := 0 to iCount - 1 // precalc buffer size...
    do Inc( iLen, Length( list[ iIndex ] ) );

  SetString( Result, nil, iLen + iCount - 1 ); // set string buffer size
  szBuf := PChar( Result ); // store temporary char pointer

  // this block saves ListBox1.Items.Count-1 if-statements in the following for-loop... :-)
  sTemp := list[ 0 ];
  iLen  := Length( sTemp );
  System.Move( PChar( sTemp )^, szBuf^, iLen); // memory copy from list string to string buffer
  Inc( szBuf, iLen );

  for iIndex := 1 to iCount - 1 do
  begin
    szBuf^ := '+';
    Inc( szBuf );
    sTemp := list[ iIndex ];
    iLen  := Length( sTemp );
    System.Move( PChar( sTemp )^, szBuf^, iLen); // memory copy from list string to string buffer
    Inc( szBuf, iLen );
  end;
end;

Usage:
  sMyString := AssembleString( ListBox5.Items );

Nothing to it, really...

/// John
0
 
LVL 4

Expert Comment

by:erajoj
ID: 1358375
HUUGE BUG ABOVE!

Should be:

begin
  // {$B-} no need, sorry!
  if ( ( not Assigned( list ) ) {>>>} OR {<<<} ( ListBox1.Items.Count = 0 ) ) then begin // no need to continue...
    Result := '';
    Exit;
  end;

/// John
0
 

Author Comment

by:aztec
ID: 1358376
Worked great John...thanks very much for your help on this!

cheers!
   Shawn
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now