|
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;
|