Delphi RSS Resources, Delphi Components, Delphi Sites, Articles, Dynamic XML Feeds, Tutorials, Sources
 
 
 
Google
 
Web delphirss.com
| Server-Scripts.com | Informations for JAVA | Informations for PHP | SEO Web Links | Borland Delphi
 

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;










© DelphiRSS.com. All Rights Reserved.