Strings

Strings

Difference between using the StrPCopy function the pointer characters
Difference between two string types
Add #0 to a pascal string
Case and Strings
LTrim, RTrim & Trim
Removing commas from string
AppendStr and ConCat
Passing strings in the lParam part of a message
Strings and messages
Type missmatch (PChar <> String)
Appending in TMemo, don't start a new line
How do I get a string out of a memo field
String handling
PChar from TMemoField
Shortened Directory label
Most Efficient way to trim a String
String manipulation question
Function to convert first character of each word in a string to uppercase
Memo, strings and arrays
Formatting strings
Floating Strings
Trimming spaces from strings
String manipulation functions


Difference between using the StrPCopy function the pointer characters

Question


What is the difference between using the strPCopy Function and just using the pointer characters ^ or @?



Answer


A:

The strPCopy function converts a Pascal length byte string into PChar

format primarily for use with Windows APIs that expect them.




Difference between two string types

Question


Is there anything wrong with the following segment of code?

var

  s : string;

  p : pstring;



begin

     s := 'Sample String';

     p := @s {or p := s^, for that matter}



Answer


A:

Nothing is "wrong" with it, but a pstring <> a PChar.



A:

There should be no real problem, but remember that the first byte of

a pascal style 'string' contains the length of the string, so you

really would want to reference the second byte.



A:



STRING <> PCHAR

You are trying to pass a string as an agruement that wants a memory

location (pointer). Either declare the parameter as String in your

function or pass a pointer to the string you are creating.

Anyway...in brief, without lecturing on memory usage, etc:

CHAR is the variable type for a SINGLE character

PCHAR is the variable type for a POINTER to A character which is NOT

limited to ONE character but can extend to 65,535 characters depending on

the memory you allocate for it -> that is the key here, it can occupy the

space you ALLOCATE or you will run-over memory and Windows will be sad.

However, remember that your are still only "pointing" to one character, it

is just that the memory that follows it has been allocated by you and can

contain other characters (you can walk down the memory by adding 1 to the

pointer as you go along - hence you are limited to the 64K PCHAR block in a

16-bit OS).



STRING is the variable type for a pre-allocated array of characters with

a max size of 255.




Add #0 to a pascal string

Question


I have seen many different pieces of code, some of which append a #0

to the end of a string etc., but this seems to work just fine for me.

If there is something wrong, please elaborate under what circumstances it

would rear it's ugly head.



Answer


A:

The problem may be that you're not clear when you're calling standard

Windows APIs that expect PChars, vs. Delphi's wrapper functions for many of

these, which usually take Pascal style strings.  My experience has been that

it's easy to get junk characters in a string or even throw a GPF if you're

not careful with how you handle this stuff.



The best "hack" for this that I know of is to pass the address of the

[1]th element of a string to the API call (this skips the length byte of

the string).  Many APIs do rely on the presence of the null byte at the

end of a string for length determination so it's wise to add it, even if

it sometimes appears to work without it.  Something like:



var

  S : string ;



begin

  S := 'This is the string to pass' + #0 ;

  SomeAPICall( @S[1] ) ;



You can also use a Pascal string as a return buffer in similar fashion

with APIs that specify a maximum buffer length and return the number of

characters retrieved as the function's value:



var

  S : string[128] ;



begin

  S[0] := Chr( SomeOtherAPICall( @S[1], 128 )) ;



Since .INI file entries and String resource items are both limited to

255 characters anyway, and it's often easier to manipulate Pascal style

strings programmatically, these techniques can be quite useful for some

of the more common Windows string purposes.




Case and Strings

Question


I am trying to use a String with the Case statement.

Answer


A:

A single Character is considered "ordinal" so, in your case, you could code

the following two level imbeded Case statements:

        Case Code[1] of

             'K':Case Code[2] of

                 'T':begin

                      {code for KTAA to KTZZ}

                     end;

                 'D':begin

                      {code for KDAA to KDZZ}

                     end;

                 END;

              'L':Case Code[2] of

        etc...

As an idea, you could turn the letters into their numeric values with

the ORD and CHR functions, and then do a case statement on them. eg:



function NumConvert(s: String): LongInt;

var

  i: Integer;

begin

   s := UpperCase(s); {work with uppercase strings}

   for i := 1 to Length(s) do

      result := result + (Ord(s[i]) * exp(((Length(s) - i) * 2) * ln(10)));

   {We have taken the ascii value of the placeholder, and multiplied it by

    it's position * 2, eg. an 'A' in the second position (as in cAke) would

    be 65 * 10 ^ ((4 - 2) * 2), which is 650000 etc. The final result should

    be 67657569 for the word 'cake'}

end;



(Note: if you're stuck for an ASCII chart, look in the DOS-QBASIC help file.

       under contents you'll find an ASCII chart, - I'm sure Delphi will

       probably have one somewhere as well. At worst, make a program to

       generate one eg: for i := 1 to 255 do ShowMessage(IntToStr(i)+Chr(i)); )



This function should generate a longint that could be used in a case statement,

for example, if you wanted everything between AA and AC, your case statement

would be:



  s := 'abracadabra';

  l := NumConvert(Copy(s, 1, 2)); {Get the first two characters of the string}

  {at this stage l should contain 6566, check this with delphi debugger...}



  case l of

    6565..6567: ShowMessage('s between AA and AC'); {Includes ACAA to ACZZ}

    6768..7171: ShowMessage('s between CD and GG'); {Includes GGAA to GGZZ}

    7065: ShowMessage('s begins with FA');




LTrim, RTrim & Trim

Question


Is there a trim function?

Answer


A:

Since I didn't find a Visual Basic LTrim, RTrim and Trim functions

equivalents in Delphi I wrote the functions that follow:



  unit GlbFuncs;



  interface



  function LTrim(sS : string) : string;

  function RTrim(sS : string) : string;

  function Trim(sS : string) : string;



  implementation



  function LTrim(sS : string) : string;

  var

    iX, iLen : integer;

  begin

    iLen := Length(sS);

    if iLen > 0 then

      begin

        iX := 1;

        while Copy(sS,iX,1) = ' ' do iX := iX + 1;

        LTrim := Copy(sS,iX,iLen-(iX-1));

      end

    else

      LTrim := sS;

  end;



  function RTrim(sS : string) : string;

  var

    iX, iLen : integer;

  begin

    iLen := Length(sS);

    if iLen > 0 then

      begin

        iX := iLen;

        while Copy(sS,iX,1) = ' ' do iX := iX - 1;

        RTrim := Copy(sS,0,iX);

      end

    else

      RTrim := sS;

  end;



  function Trim(sS : string) : string;

  begin

    Trim := LTrim(RTrim(sS));

  end;



  end.



A:

 unit TrimStr;

 {$B-}

 {

      File: TrimStr

    Author: Bob Swart [100434,2072]

   Purpose: routines for removing leading/trailing spaces from strings,

            and to take parts of left/right of string (a la Basic).

   Version: 2.0



   LTrim()    - Remove all spaces from the left side of a string

   RTrim()    - Remove all spaces from the right side of a string

   Trim()     - Remove all extraneous spaces from a string

   RightStr() - Take a certain portion of the right side of a string

   LeftStr()  - Take a certain portion of the left side of a string

   MidStr()   - Take the middle portion of a string



 }

 interface

 Const

   Space = #$20;



   function LTrim(Const Str: String): String;

   function RTrim(Str: String): String;

   function Trim(Str: String):  String;

   function RightStr(Const Str: String; Size: Word): String;

   function LeftStr(Const Str: String; Size: Word): String;

   function MidStr(Const Str: String; Size: Word): String;



 implementation



   function LTrim(Const Str: String): String;

   var len: Byte absolute Str;

       i: Integer;

   begin

     i := 1;

     while (i <= len) and (Str[i] = Space) do Inc(i);

     LTrim := Copy(Str,i,len)

   end {LTrim};



   function RTrim(Str: String): String;

   var len: Byte absolute Str;

   begin

     while (Str[len] = Space) do Dec(len);

     RTrim := Str

   end {RTrim};



   function Trim(Str: String): String;

   begin

     Trim := LTrim(RTrim(Str))

   end {Trim};



   function RightStr(Const Str: String; Size: Word): String;

   var len: Byte absolute Str;

   begin

     if Size > len then Size := len;

     RightStr := Copy(Str,len-Size+1,Size)

   end {RightStr};



   function LeftStr(Const Str: String; Size: Word): String;

   begin

     LeftStr := Copy(Str,1,Size)

   end {LeftStr};



   function MidStr(Const Str: String; Size: Word): String;

   var len: Byte absolute Str;

   begin

     if Size > len then Size := len;

     MidStr := Copy(Str,((len - Size) div 2)+1,Size)

   end {MidStr};

 end.


Removing commas from string

Question


Is there an easy way to remove commas from a string of numbers (e.g.

489,356,456) so that the resulting string can be converted to an integer using

the strtoint function?

Answer


A:

var

   p : integer

begin

   repeat

      p := pos(',',str);

      if p>0 then

        delete(str,p,1);

   until p=0;

end;



A:

function stripped(stripchar : char, str : string) : string;

var

  tmpstr : string;   { can't modify a value parameter and }

                     { may not want to directly change the source }

begin

  tmpstr := str;

  while pos(stripchar, tmpstr) > 0 do

    delete(tmpstr, pos(stripchar, tmpstr), 1);

  stripped := tmpstr;

end;



Use it like this:



Edit2.Text := stripped(',', Edit1.Text);



A:

Function StripStr(S : String) : String;

Var

  bI : Byte;

Begin

  bI:=1;

  While (bI length( s ) ) then

                                move( S[x+1], S[x], length( S ) - succ(x) );

                                (* you can use "delete( S, x, 1 );" instead

of the above move() *)

                        dec( byte( s[0] ) );

                end

                else

                        inc( x );

        end;

end;



A:

For I:= Length(S) downto 1 do

  begin

    if S[I] = ',' then

      delete(S,I,1)

  end



This does not need any extra strings, is very fast, and will cope with

adjacent commas (which some of the proferred examples wont - the key is the

downto).



I have used the algorithm for years in Turbo pascal to strip unwanted blanks

from strings.  One could even use



   if not (S[I] in ['0'..'9']) then



to remove any other rubbish


AppendStr and ConCat

Question


Can anyone please explain to me why there is such a redundancy of procedures

and functions in Delphi? Why are, for example, AppendStr and Concat almost

identical in their operation and, more, when you consult the help for the one,

there is no cross reference (see also) for the other? Is there some essential

difference between AppendStr and Concat that is not obvious from the Help?

Answer


A:

There are usually subtle differences, and the help does a reasonable job of

highlighting these. For example, Concat can concatenate 2 OR MORE strings, and

does it with similar efficiency to using the + operator. On the other hand,

AppendStr only adds one string to another, but does it a bit more quickly.



Of course much of the proliferation of string handling routines is due to the

difference in philosophy between Pascal and Windows i.e. byte-counted vs. null-

terminated, and the need to provide conversion back and forth.



I guess that some of the duplication also arises because of the need to ensure

backwards compatibility when adding improved routines.



I agree about the "See Also" popup windows. Quite a few useful cross-references

have been omitted. But on the whole I think Borland did a brilliant job of the

on-line help.



A:

AppendStr combines one string with another.  Concat allows you to

combine many string into one. i.e. Concat(s,s1,s2,s3,etc)


Passing strings in the lParam part of a message

Question


My problem is how to pass a string in a message. If I remember correctly,

this can only be done in the lParam part, but there is something missing

(Because it's not working!).

Answer


A:

In the Win16 API, lParam is a longint (32-bit value), and wParam is a word

(16-bit value). So the only way to pass a string using lParam is by passing

a pointer to it. The Windows-defined messages (such as WM_SetText, for

example) are going to use a null-terminated string, which translates to a

PChar in Delphi. So if you're going to pass a string using one of the

standard windows messages you'll have to translate from a pascal string to a

PChar, like this:



procedure SetText(aWnd: hWnd; Value: string);

var

  p: array[0..255] of char;

begin

  StrPCopy(p, Value);

  SendMessage(aWnd, wm_SetText, 0, Longint(p));

end;



Note that in the above example it is necessary to 'type-cast' the PChar 

(which is essentially a pointer) to a longint, so the compiler won't reject 

it. Also, its rather interesting that the compiler understands an 'array[]

of char' to be the same thing as a PChar. In the above example, the StrPCopy

function (in SysUtils) specifies that the first parameter needs to be of type

PChar, but the type I passed to it looks like an array of char. So why

didn't I declare p as a PChar? Because declaring it as a PChar would only be

declaring a *pointer* to a string, whereas I needed to actually allocate the

space to store the string. I could also have done something like:



procedure SetText(aWnd: hWnd; Value: string);

var

  p: PChar;

begin

  GetMem(p, 256);

  try

    StrPCopy(p, Value);

    SendMessage(aWnd, wm_SetText, 0, Longint(p));

  finally

    FreeMem(p, 256);

  end;

end;



This would accomplish the same thing, but instead of allocating the storage

for the null-terminated string on the stack, this stores it on the heap.  And

this is fine, but I like the first example better because I don't have to

worry about cleaning up afterward, and it should be faster, too. Now, what

if I had used the PostMessage instead of SendMessage in the above example?

If I did that, I would probably get a GPF! Why? Because PostMessage only

places the message in a queue, while SendMessage delivers it before

continuing on to the next line. So by placing it in a queue, that means I

would continue on and deallocate the storage for the string before it

actually got delivered! So, you should almost always use SendMessage when

communicating with Windows controls (if you're going to do it the

old-fashioned way, that is!).



If you're talking about responding to a Windows message that uses a PChar in

lParam, you could break that out like this:



type

  TMyComponent = class(TWinControl)

  private

    procedure wmMyMessage(var Msg: Message); message wm_MyMessage;

  end;



procedure TMyComponent.wmMyMessage;

var

  st: string;

begin

  st := StrPas(PChar(Msg.lParam));

end;



Anyway, if you want to define your own user-defined messages, you could 

decide that you want to pass a pointer to a pascal string (PString) rather 

than a pointer to a null-terminate string (PChar). In this case you could

send the message like this:



SendMessage(aWnd, wm_MyMessage, 0, Longint(@MyString));



where MyString is a pascal string. Then in the component that receives the

message, you could typecast the lParam to PString, and dereference using the

'^'.  Like this:



procedure MyMessage(var Msg: Message);

begin

  Label1.Caption := PString(Msg.lParam)^;

end;



Whew!  This turned into a long message! As you can see, there a lot of

different things you can do with this. In fact, you can even pass whole

records, or a pointer to an object which then would allow you to access any

of its properties or methods! (But not from a DLL, sorry.) If you have any

more questions on this, feel free to ask. I've had quite a bit of experience

with this.


Strings and messages

Question


I have an app which sends a message:



var

  strSend: String;

begin

  strSend:=dfEdit.Text;

  PostMessage (hWndDest, PM_Update, 0, longint(@strSend));

end;



My Receiving app has the following:



with msg do

  dfReceive.Text := strPas (PChar(lParam));



The problem is as follows:

  The string received/displayed is always prefixed with an unprintable 

character, which gets displayed as a square box. The other problem is that 

if the first string sent is, for example 'HELLO', and the second string

sent is 'IAN', the output will be 'IANLO' (With the square box as well).

Answer


I see two problems with the example you give.  The first one is you're mixing

Pascal strings and PChars.  You should stick with one or the other.  Also, if

you're allocating storage for the string on the stack local to a procedure or 

function), and you use PostMessage, you will likely have problems because it 

will be deallocated before the message is received.  Try this:



procedure TForm2.Button1Click(Sender: TObject);

var

  st: PString;

begin

  st := NewStr(Edit1.Text);

  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(st));

end;



procedure TForm1.amMyMessage(var Msg: TMessage);

var

  st: PString;

begin

  st := PString(Msg.lParam);

  Label1.Caption := st^;

  DisposeStr(st);

end;



The above should work as long as the message is sent and received in the same 

app.  And this should work in Delphi32 as well.  The problem with sending it 

between applications is that Delphi has a memory suballocator which will get 

all confused if you try to free the memory from another app.  If you must

send it between apps, you should either make sure the string is allocated

statically (by placing it outside of any object declaration and outside of 

any procedure or function, and don't use NewStr and DisposeStr), or allocate 

and deallocate it using GlobalAlloc and GlobalFree, which are the Windows API 

functions for that purpose.  To declare it statically, do the following:



var

  TempStorage: string;



procedure TForm2.Button1Click(Sender: TObject);

begin

  TempStorage := Edit1.Text;

  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(@TempStorage));

end;



In second app, 



procedure TForm1.amMyMessage(var Msg: TMessage);

begin

  Label1.Caption := PString(Msg.lParam)^;

end;



Of course, you won't have the problem of the memory being deallocated before 

the message arrives if you can use SendMessage instead.  In that case, you 

could do the following for the first method:



procedure TForm2.Button1Click(Sender: TObject);

var

  TempStorage: string;

begin

  TempStorage := Edit1.Text;

  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(@TempStorage));

end;



and leave the second one alone.


Type missmatch (PChar <> String)

Question


I need to pass a string variable to a procedure which is expecting a String

constant (Type PChar to be exact).

Answer


A:

You have a couple of options, the cannonical (and presumably future

portable) method is to use the StrPCopy() function to convert the string

to a PChar (just be sure to allocate space for the PChar!).



A quick hack that will also work relies on the fact that both Pascal and

C style strings are, at base, arrays of characters.  Most of the Windows

API calls that want PChars expect the passed string to be null (ASCII

zero) terminated, though some want a length, you have to check on that;

anyhoo, some code like the following should work.



var

  S : string ;



begin

  S := {whatever} ;

...



  S[Length(S) + 1 ] := #0

  { add the null terminator, be careful not to write past }

  { the end of the full allocated length of S (255 if a   }

  { full string) lest you wish to incur the wrath of the  }

  { GPF gods :-)   }



  WinAPICall( ..., @S[1], SizeOf( S ), ...) ;

  { index past the length byte at S[0] }


Appending in TMemo, don't start a new line

Question


I've got 2 strings, each 210 bytes in length. I want to fill a memo box with

this stuff. Unfortunately when I do:



         Memo1.Lines.Add(var1);

         Memo1.Lines.Add(var2);

Answer


A:

The TMemo class has a property called WordWrap.  You need to set it's

value to FALSE.  This should fix the problem.



A:

The help text for TMemo says:



    "You can...access the text line by line using the Lines

    property. If you want to work with individual lines of text,

    the Lines property will suit your needs better.  If you want

    to work with the text as one chunk, use the Text property."



But, as you've probably discovered, the Text property seems to

be a string and is therefore limited to 255 bytes. So you may

_have_ to work with Lines. In this case you'll sometimes need to

concatenate the strings then add them e.g.



    Memo1.Lines.Add(var1+var2);


How do I get a string out of a memo field

Question


I have a memo field in a table that I know is less than 255 bytes.

I need to stuff it into a string and nothting I do seems to work.

Answer


A:

  Memos := TStringList.Create;

  Memos.Assign(Table1Memo);

  MyString := '';

  for I:= 0 to Memos.Count-1 do

    MyString := MyString + Memos[I];

  Memos.Free;



A:

if Memo1.GetTextLen <= 255 then

  aStr := TEdit(Memo1).Text;



If you look at the source for TEdit, TCaption, and TMemo, none of them have a

property called Text or Caption; it is inherited from TControl.  TCaption

publishes Caption, while TEdit publishes Text.  TMemo doesn't publish either,

but we know the property is there, so we can get access to it by pretending

that the Memo object is a TEdit object (through a typecast), thus bypassing

the protection.  If the typecast offends you, do it like this:



function TForm1.GetStr: string;

var

  st: array[0..256] of char;

begin

  Memo1.GetTextBuf(st, sizeof(st));

  Result := StrPas(st);

end;



A:

When I referenced the string list from form one (see above ex. code) I was using the

.PAS filename for form1.  Turns out, I needed to use the Tform name instead - i.e.,

instead of FORM1 I needed to use FRMORDENTRY:



         If (frmOrdEntry.List1[0] < '00000' ) or (frmOrdEntry.List1[0] < '    0') then

                 Listbox1.Items.Add(frmOrdEntry1.List1[0]);





Funny thing is, in another section of code on Form2, I HAD to use the .PAS filename

to reference an array from form one.


String handling

Question


I am writing a couple of string functions to mimic those found in Basic,

specifically:Space$ and String$ functions. These functions fill a string with

a given number of Spaces or other character. Do these functions already exist

in Pascal? If not, what is a good, fast way to implement these functions.

Answer


A:

You can try



Function String(number : integer; ch : char) : string;

begin

     if number > 255 then

          number := 255;

     result[0] := chr(number);

     fillchar(result[1], number, ch);

     end;



Function Space(number : integer) : string;

begin

     if number > 255 then

          number := 255;

     result[0] := chr(number);

     fillchar(result[1], number, ' ');

     end;



A:

FUNCTION String$(C : Char; Len : Byte) : String;

VAR Temp : String;

BEGIN

  FillChar(Temp[1], Len, C);

  Temp[0] := Char(Len);

  Result := Temp;

END;



FUNCTION AString$(C : Char; Len : Byte) : String; Assembler;

ASM

  LES DI, @Result

  CLD

  XOR CH, CH

  MOV CL, Len

  MOV AX, CX

  STOSB

  MOV AL, C

  REP STOSB

END;



A:

Probably the fastest thing to do without resorting to assembler is to

use FillChar(), but you have to be careful that you don't overwrite

anything that doesn't belong to you (lest you wish to incur the wrath of

the GPF gods), and to make sure that you keep track of all the "string

stuff"; with those caveats, something like the following ought to work:



function StringS( n : byte ; ch : char ) : string ;

var

  S : string ;

begin

  FillChar( S[1], n, ch ) ;

  (* having passed n as a byte protects us here from overwriting *)

  (* memory that doesn't belong to us                            *)



  S[0] := Chr( n ) ;         (* keep track of the string length *)



  StringS := S ;

end ;



function SpaceS( n : byte ) : string ;

begin

  SpaceS := StringS( n, ' ' ) ;

  (* of course you could simply recapitulate the StringS() code here *)

  (* replacing ch with ' ', which would save a function call         *)

end ;


PChar from TMemoField

Question


How can I get the text from a TMemoField as a PChar?

Answer


A:

Get size of data for PChar:



function TBWF.TMemoFieldSize(Memo: TMemoField): Word;

var

  BS: TBlobStreeam;

begin

  BS := TBlobStream.Create(TMemoField(Memo), bmRead);

  Result := BS.Size;

  BS.Free;

end;



Create PChar with Size + 1:



Assign Data from Field to PChar:



procedure TBWF.TMemoFieldToPChar(Memo: TMemoField; var Buffer: PChar; Size:

Word);

var

  BS: TBlobStream;

begin

  try begin

    BS := TBlobStream.Create(TMemoField(Memo), bmRead);

    FillChar(Buffer^, Size, #0);

    BS.Read(Buffer^, Size);

    end;

  finally

    BS.Free;

  end;

end;



A:

function GrabMemoFieldAsPChar(TheField : TMemoField): PChar;

begin

GetMem(Result, TheField.Size + 1);

FillChar(Result^, TheField.Size + 1, #0);

with TBlobStream.Create(TheField, bmRead) do

  begin

    Read(Result^, TheField.Size);

    Free;

  end;

end;


Shortened Directory label

Question


If the directory label is:

c:\windows\media\temp\abc\sound\chime.wav

I would like the label to appear as:

c:\windows\..\sound\chime.wav

and not the whole chunk of filename.

Is there any way to accomplish this easily?

Answer


A:

I developed a procedure, that does something like that.

It shortens the path, when it and the current path have

the same drive and/or directory in parts.

It's really useful for making the pathname easier to read

and understand.

I've written it for a hex-editor in Borland Pascal and I haven't been

using it for a while, but it should work flawlessly.



function shortenfilename(s : string) : string;

var drive,curdrive : string[2];

    dir,curdir : string[80];

    name : string[20];

    ext : string[5];

    i : byte;

begin

  for i:=1 to length(s) do s[i]:=upcase(s[i]);

  s:=fexpand(s);

  fsplit(s,dir,name,ext);

  drive:=copy(dir,1,2);

  dir:=copy(dir,4,length(dir)-3);

  getdir(0,curdir);

  curdrive:=copy(curdir,1,2);

  curdir:=copy(curdir,4,length(curdir)-3)+'\';

  if drive=curdrive then begin

    if copy(dir,1,length(curdir))=curdir then begin

      i:=length(curdir);

      if length(dir)<>i then dir:=dir+'\';

      shortenfilename:=copy(dir,i+1,length(dir)-i-1)+name+ext;

    end else shortenfilename:=copy(s,3,length(s)-2);

  end else shortenfilename:=s;

end;


Most Efficient way to trim a String

Question


Anyone has a _very_ fast algorithm to Right-trim, left-trim,

and All-Trim a string?

Answer


A:

function PTrim(PS: PChar): Integer;

var ArCh: PByteArray absolute PS;

    i,f: Integer;

begin

  if ArCh^[0] = 0 then

  begin

    PTrim := 0;

    Exit;

  end;

  i:=0;

  f := StrLen(PS);

  while (f > 0) and (ArCh^[f-1] = 32) do Dec(f);

  while (i < f) and (ArCh^[i] = 32) do Inc(i);

  StrLCopy(PS, PS+i, f-i);

  PTrim := f-i;

end;

===============================================

PROCEDURE Trim(VAR S : String; C : Char);

BEGIN

  if S = '' then Exit;

  WHILE S[length(S)] = C DO Dec(S[0]);

END;

===============================================

PROCEDURE TrimLead(VAR S : String; C : Char);

VAR P : Byte;

BEGIN

  if S = '' then Exit;

  P := 1;

  WHILE (S[P] = C) AND (P <= length(S)) DO Inc(P);

  CASE P OF

    0 : S[0] := #0; {la stringa era 255 di C!}

    1 : ; {non trovato}

    ELSE

      Move(S[P], S[1], succ(length(S) - P));

      Dec(S[0], pred(P));

  END;

END;

=====================================================

function Trim(S : string) : string;

var I : Word;

    SLen : Byte absolute S;

begin

  while (SLen > 0) and (S[SLen] <= ' ') do Dec(SLen);

  I := 1;

  while (S[I] <= ' ') and (I <= SLen) do Inc(I);



  CASE I OF

    0 : SLen := 0;  {was of 255 blanks}

    1 : ;

    ELSE Move(S[I], S[1], succ(SLen - I));

         Dec(SLen, pred(I));

  END;

  Trim := S;

end;



A:

procedure Trim(var s: String);

var

  i: Integer;

begin

{ s > '' ? }

  if Length(s)>0 then begin

{ Trim leading }

    i:=1;

    While (Length(s)>=i) and (s[i]<=' ') do

      Inc(i);

    if i>Length(s) then begin

{ Trap the empty string, remove all and exit }

      s:='';

      Exit;

    end;

    if i>1 then begin

      s[0]:=Chr(Length(s)-i+1);

      Move(s[i],s[1],Length(s));

    end;

{ Trim Trail }

    i:=Length(s);

    While (i>0) and (s[i]<=' ') do

      Dec(i);

    s[0]:=Chr(i);

  end;

end;


String manipulation question

Question


I would like to create a series of strings (let's say 1-3 characters

long) that would look like this:

A

B

C

...

X

Y

Z

AA

AB

AC

...

AZ

BA

BB

...

ZX

ZY

ZZ

AAA

AAB

...

ABA

...

BAA

...

ZZZ

I want to then pass this into a function that will analyze the string.

I'm thinking that I need to create a recursive function, but this also

seem s to be solvable through a set of FOR loops.  I'm stuck right now.

I need a kickstart in the right direction.

Answer


In your private section of your form put the following declarations:



PassStrings: TStringList;

procedure FillStrings;



now put the following procedure which you will call somewhere in your code:



procedure TForm1.FillStrings;

var

  i, j, k : Integer;

  c, c1, c2 : Char;

  s : String;

begin

  {do the single a to z}

  for i := 0 to 25 do

    begin

      s := '';

      c := Char(Ord(i)+ ord('A'));

      s := s + c;

      PassStrings.Add(s);

    end;



  {now do the double a to z}

  for i := 0 to 25 do

    begin

      s := '';

      c := Char(Ord(i)+ ord('A'));

      s := s + c;

      for j := 0 to 25 do

        begin

          c := Char(Ord(j)+ ord('A'));

          PassStrings.Add(s+c);

        end;

    end;



  for i := 0 to 25 do

    begin

      s := '';

      c := Char(Ord(i)+ ord('A'));

      s := s + c;

      for j := 0 to 25 do

        begin

          c1 := Char(Ord(j)+ ord('A'));

          for k := 0 to 25 do

            begin

              c2 := Char(Ord(k)+ ord('A'));

              PassStrings.Add(s+c1+c2);

            end;

        end;

    end;

end;


Function to convert first character of each word in a string to uppercase

Question


I need a function to convert the first character of each word in a

string to an uppercase character. Is there such a function - or has

anyone already accomplished this.

Answer


Function Proper(const s:String): String;

var

 index: Byte;

 First:Boolean;

 

 begin

   Result:=S;

   First:=True;

   For Index :=1 to Length(s) do 

    begin

     If First then

      Result[Index] := UpCase(Result[Index]);

    If Result[Index]  = ' ' then

     First:=True;

   else

    First:=False;

 end;

end;



A:

put this code in your Keypress event handler:



with Edit1 do

	if ( SelStart =0 ) or ( Text [SelStart] =  ' '  )  {space}

	then

		Key  := UpCase (Key) ;


Memo, strings and arrays

Question


Is it possible to access each character of each string, one at a time, from

a memo. I want to check the characters of each string one at a time, I am

sure that there has to be a way.

Answer


The Lines property of a TMemo is a TStringList; which makes the lines in

the memo (as long as they're less than 255 characters long) look more or

less like an array of strings, where you can access those strings

character by character just like if they were individual strings;

however, the individual lines are read only, so you have to do a

temporary storage, then bait and switch, to MODIFY the lines as strings;



procedure TForm1.ManipulateMemo( TheMemo : TMemo ;

  SomeChar, OtherChar : char  ) ;

var

  i : word ;

  j : byte ;

begin

  { StringLists are indexed from 0 }

  for i := 0 to TheMemo.Lines.Count - 1 do

  begin

    Temp := TheMemo.Lines[i] ;

    for j := 1 to Length( Temp ) do



      if Temp[j] = SomeChar then   { or whatever kind of character by }

        Temp[j] := OtherChar ;     { character manipulations you need }



    TheMemo.Lines.Insert( i, Temp ) ; { insert modified line at current pos}

    TheMemo.Lines.Delete( i + 1 ) ;  { remove old version of line }

  end ;

end ;



Note that this is a real basic solution, and is neither tested (I just

wrote this off the top of my head, though I've done other routines like

this many times) or optimized (you could probably speed things up

somewhat by checking for the presence of characters of interest in the

individual lines using Pos() before doing the copy and indexing on the

individual characters).



A:

Use the lines property.



For i := 0 To mymemo.lines.count - 1 Do

Begin

  For j := 1 To Length(mymemo.lines[i] Do

  Begin

    {Do something with mymemo.lines[i][j]}

  End;

End;


Formatting strings

Question


I need a function which sets strings maximum size to n, (the string length

is less than n) and also in the result the original one should be aligned to

right.

Is there a ready function for that/what would be the best and efficient

way to do that?

Answer


function padleft(S: String; N: Integer): String;

begin

  While Length(S) < N do

    Insert(' ',S,1);

  Result:=S

end;



function padright(S: String; N: Integer): String;

begin

  While Length(S) < N do

    Insert(' ',S,Length(S)+1);

  Result:=S

end;


Floating Strings

Question


I need to put the floating point value into a FP

variable only IF the string is a valid FP-value.

If it's not, the code in the ELSE-part of my if sentence should be

executed.

And the last thing is very important: NO msgdlgs to the user! This if

sentence is to be performed, MANY times.

So what I need is some way to test wheather the string is a Floating point

value or not, and without displaying anything to the user.

Answer


A:

function TestFloat(const s : string): boolean;

var

  i : integer;

  x : double;

begin

  System.Val(s,x,i);

  Result := i<>0;

end;



As you are Danish you should note that val does not accept ","

as decimal separator. So maybe you prefer this one:



function TestFloat(s : string): boolean;

var

  i : integer;

  x : double;

begin

  i := System.Pos(',',s);

  if i<> 0 then s[i] := '.'; 

  System.Val(s,x,i);

  Result := i<>0;

end;



A:

function IsStringFloatNumber(S: string) : boolean;

   var TempFloat : Extended;

begin

   try

      TempFloat :=3D StrToFloat(S);

      Result :=3D True;

   except

      Result :=3D False;

   end;

end;       =20



A:

function IsValidFloat( s : string ) : Boolean

var

  TempFloat : Real;

begin

  Result := True;

  try

    TempFloat := StrToFloat(s);

  except

    Result := False;

  end;

end;


Trimming spaces from strings

Question


How to trim spaces from string?

Answer


  unit TrimStr;

{ 

  NOTE: The compiler must be in the default mode for boolean evaluation, i.e.,

  short-circuiting 

}

  interface

  Const

    Space = #$20;



    function LTrim(Str: String): String;

    function RTrim(Str: String): String;

    function Trim(Str: String):  String;



  implementation



    function LTrim(Str: String): String;

    var len: Byte absolute Str;

        i: Integer;

    begin

      i := 1;

      while (i <= len) and (Str[i] = Space) do Inc(i);

      LTrim := Copy(Str,i,len-i+1)

    end {LTrim};



    function RTrim(Str: String): String;

    var len: Byte absolute Str;

    begin

      while (Str[len] = Space) do Dec(len);

      RTrim := Str

    end {RTrim};



    function Trim(Str: String): String;

    begin

      Trim := LTrim(RTrim(Str))

    end {Trim};

  end.





Groetjes,

          Dr. Bob



-------------------------------------------------------------------------------



From: pierre@panpan.synapse.net (Pierre Tourigny)

Subject: Re: Any simple way to trim spaces from a string? (... Delphi related ...)

Date: Fri, 31 Mar 1995 09:29:26 LOCAL



The ltrim function doesn't work correctly when the string is all spaces. 

Replace i < len with i <= len. This works because copy returns an empty 

string when i > len. 

(N.B.: The compiler must be in the default mode for boolean evaluation, i.e.,

short-circuiting).






String manipulation functions

Question




Answer


{To determine if the character is a digit.}

function IsDigit(ch: char): boolean;

begin

  Result := ch in ['0'..'9'];

end;



{To determine if the character is an uppercase letter.}

function IsUpper(ch: char): boolean;

begin

  Result := ch in ['A'..'Z'];

end;



{To determine if the character is an lowercase letter.}

function IsLower(ch: char): boolean;

begin

  Result := ch in ['a'..'z'];

end;



{Changes a character to an uppercase letter.}

function ToUpper(ch: char): char;

begin

  Result := chr(ord(ch) and $DF);

end;



{Changes a character to a lowercase letter.}

function ToLower(ch: char): char;

begin

  Result := chr(ord(ch) or $20);

end;



{ Capitalizes first letter of every word in s }

function Proper(const s: string): string;

var

  i: Integer;

  CapitalizeNextLetter: Boolean;

begin

  Result := LowerCase(s);

  CapitalizeNextLetter := True;

  for i := 1 to Length(Result) do

  begin

    if CapitalizeNextLetter and IsLower(Result[i]) then

      Result[i] := ToUpper(Result[i]);

    CapitalizeNextLetter := Result[i] = ' ';

  end;

end;



{ NOTE: The following functions are available in Delphi 2.0,

  but not in Delphi 1.0. }



{Supresses trailing blanks in a string.}

function TrimRight(const s: string): string;

var

  i: integer;

begin

  i := Length(s);

  while (I > 0) and (s[i] <= ' ') do Dec(i);

  Result := Copy(s, 1, i);

end;



{Removes the leading spaces from a string.}

function TrimLeft(const S: string): string;

var

  I, L: Integer;

begin

  L := Length(S);

  I := 1;

  while (I <= L) and (S[I] <= ' ') do Inc(I);

  Result := Copy(S, I, Maxint);

end;



{ Removes leading and trailing whitespace from s);

function Trim(const S: string): string;

var

  I, L: Integer;

begin

  L := Length(S);

  I := 1;

  while (I <= L) and (S[I] <= ' ') do Inc(I);

  if I > L then Result := '' else

  begin

    while S[L] <= ' ' do Dec(L);

    Result := Copy(S, I, L - I + 1);

  end;

end;




Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > delphi > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction