Quite a silly question, but I've been stuck on this for quite a while now. I want to copy the first letter of a string and store it in a char variable, then test whether this variable is uppercase or lowercase with the use of an 'IN' statement. I get an error of incompatible types when I try to store the first letter into a char though, and I'm not exactly sure as to how I would get around this.
var
S: string;
C: char;
begin
S := ...;
C := S[1];
if C in ['A'..'Z'] then
begin
...
end
else if C in ['a'..'z'] then
begin
...
end;
end;
That being said, note that Char is an alias for WideChar in Delphi 2009+, and you shouldn't use Wide characters in Sets, as they will get truncated. In this case, you can use the CharInSet() function instead:
uses
..., SysUtils;
var
S: string;
C: char;
begin
S := ...;
C := S[1];
if CharInSet(C, ['A'..'Z']) then
begin
...
end
else if CharInSet(C, ['a'..'z']) then
begin
...
end;
end;
Or better, use the IsUpper() and IsLower() functions:
uses
..., Character;
var
S: string;
C: char;
begin
S := ...;
C := S[1];
if IsUpper(C) then
begin
...
end
else if IsLower(C) then
begin
...
end;
end;
uses
..., Character;
var
S: string;
begin
S := ...;
if IsUpper(S, 1) then
begin
...
end
else if IsLower(S, 1) then
begin
...
end;
end;
Related
I need to use different procedure depending on the Edittext.text
i need to call ProcessA(value: string); with Parameters of last 4 Char of string if Edittext.text's first 4 char are string and next 4 are number.
and Call ProcessB(value:integer) with last four numbers as parameter if all 8 char are numbers ?
For Example: If EditText.Text is ASDF1234 then i'll call ProcessA
and if EdiText.Text is 12345678 then i need to call ProcessB.
Show Error if string is like ASD12345 or ASDFG123 or 1234567A or if Numbers are in decimal.
How can i verify this ?
var
Text: string;
function CharInRange(C, FirstC, LastC: char): boolean; inline;
begin
Result := (C >= FirstC) and (C <= LastC);
end;
function IsDigitsOnly(const S: string;
FirstIdx, LastIdx: integer): boolean;
var
I: integer;
begin
for I := FirstIdx to LastIdx do
begin
if not CharInRange(S[I], '0', '9') then
begin
Result := False;
Exit;
end;
end;
Result := True;
end;
function IsUpcaseLettersOnly(const S: string;
FirstIdx, LastIdx: integer): boolean;
var
I: integer;
C: char;
begin
for I := FirstIdx to LastIdx do
begin
C := S[I];
if not CharInRange(C, 'A', 'Z') then
begin
Result := False;
Exit;
end;
end;
Result := True;
end;
procedure BadInput;
begin
raise Exception.Create('Bad Input');
end;
begin
Text := EditText.Text;
if Length(Text) <> 8 then
begin
BadInput;
end
else if IsUpcaseLettersOnly(Text, 1, 4)
and IsDigitsOnly(Text, 5, 8) then
begin
ProcessA(Copy(Text, 5, 4));
end
else if IsDigitsOnly(Text, 1, 8) then
begin
ProcessB(StrToInt(Copy(Text, 5, 4)));
end
else
begin
BadInput;
end;
end;
Alternatively
uses
..., System.RegularExpressions;
var
Text: string;
begin
Text := EditText.Text;
// I know this can be done better using a
// single regex expression with capture groups,
// but I don't know the correct code to do that...
if TRegEx.IsMatch(Text, '^[A-Z]{4}[0-9]{4}$') then
begin
ProcessA(Copy(Text, 5, 4));
end
else if TRegEx.IsMatch(Text, '^[0-9]{8}$') then
begin
ProcessB(StrToInt(Copy(Text, 5, 4)));
end
else
begin
raise Exception.Create('Bad Input');
end;
end;
How can I use varargs to print out multiple strings? I tried this but I cannot determine the size of the array. It just prints garbage.
program Project1;
{$APPTYPE CONSOLE}
{$POINTERMATH ON}
function _Print(const S: String): string; cdecl;
var
Args: Array[0..100] of Pointer absolute S;
I: Integer;
begin
I := 0;
while Args[I] <> nil do
begin
WriteLn(PString(#Args[I])^);
Inc(I);
end;
end;
const Print: function(const S: String): string; cdecl varargs = _Print;
var
A, B: String;
begin
A := 'ABC';
B := 'CDE';
Print(a, b, 'asdasd', 'fasd', ' ')
end.
A varargs function has no automated way to determine the number of arguments being passed, because only the caller knows how many parameters it is putting on the call stack. The function must determine the arguments manually, either by:
requiring the caller to pass the actual number of parameter as a fixed parameter:
function _Print(NumStrings: Integer; const Strings: string): string; cdecl;
var
Args: Array[0..100] of Pointer absolute Strings;
I: Integer;
begin
for I := 0 to NumStrings-1 do
begin
WriteLn(PString(#Strings[I])^);
end;
end;
const
Print: function(NumStrings: Integer; const Strings: string): string; cdecl varargs = _Print;
var
A, B: String;
begin
A := 'ABC';
B := 'CDE';
Print(5, a, b, 'asdasd', 'fasd', ' ');
end.
putting a sentry value at the end of the parameter list that the function can then look for. Your function is already coded for this (it is looking for a nil pointer), so just pass one:
Print(a, b, 'asdasd', 'fasd', ' ', nil);
That being said, either approach is subject to caller error and thus potentially dangerous if misused, which is why varargs-style functions are not used very often. You should consider using an open-array parameter instead:
program Project1;
{$APPTYPE CONSOLE}
{$POINTERMATH ON}
function _Print(const Args: array of string): string;
var
I: Integer;
begin
for I := Low(Args) to High(Args) do
begin
WriteLn(Args[I]);
end;
end;
const
Print: function(const Args: array of string): string = _Print;
var
A, B: String;
begin
A := 'ABC';
B := 'CDE';
Print([a, b, 'asdasd', 'fasd', ' ']);
end.
I have this problem: if I have, for example, these values: 'AA', 'AB', 'AC', 'BC' - can I define MyType that can contain only these values?
I want to do in mode that:
type MyType = ... ; // something
var X: MyType;
begin
x := 'AA' ; // is valid, 'AA' is included in X
X := 'SS' ; // not valid, 'SS' not is included in X, than raise an exception.
end;
How can I solve it? Is there some solution directly using type data?
This is actually rather simple using operator overloading.
Do
type
TMyType = record
private
type
TMyTypeEnum = (mtAA, mtAB, mtAC, mtBC);
var
FMyTypeEnum: TMyTypeEnum;
public
class operator Implicit(const S: string): TMyType;
class operator Implicit(const S: TMyType): string;
end;
implementation
class operator TMyType.Implicit(const S: string): TMyType;
begin
if SameStr(S, 'AA') then begin result.FMyTypeEnum := mtAA; Exit; end;
if SameStr(S, 'AB') then begin result.FMyTypeEnum := mtAB; Exit; end;
if SameStr(S, 'AC') then begin result.FMyTypeEnum := mtAC; Exit; end;
if SameStr(S, 'BC') then begin result.FMyTypeEnum := mtBC; Exit; end;
raise Exception.CreateFmt('Invalid value "%s".', [S]);
end;
class operator TMyType.Implicit(const S: TMyType): string;
begin
case S.FMyTypeEnum of
mtAA: result := 'AA';
mtAB: result := 'AB';
mtAC: result := 'AC';
mtBC: result := 'BC';
end;
end;
Now you can do
procedure TForm1.Button1Click(Sender: TObject);
var
S: TMyType;
begin
S := 'AA'; // works
Self.Caption := S;
S := 'DA'; // does not work, exception raised
Self.Caption := S;
end;
Delphi 7
How do i remove leading zeros in a delphi string?
Example:
00000004357816
function removeLeadingZeros(ValueStr: String): String
begin
result:=
end;
Code that removes leading zeroes from '000'-like strings correctly:
function TrimLeadingZeros(const S: string): string;
var
I, L: Integer;
begin
L:= Length(S);
I:= 1;
while (I < L) and (S[I] = '0') do Inc(I);
Result:= Copy(S, I);
end;
function removeLeadingZeros(const Value: string): string;
var
i: Integer;
begin
for i := 1 to Length(Value) do
if Value[i]<>'0' then
begin
Result := Copy(Value, i, MaxInt);
exit;
end;
Result := '';
end;
Depending on the exact requirements you may wish to trim whitespace. I have not done that here since it was not mentioned in the question.
Update
I fixed the bug that Serg identified in the original version of this answer.
Use JEDI Code Library to do this:
uses JclStrings;
var
S: string;
begin
S := StrTrimCharLeft('00000004357816', '0');
end.
Probably not the fastest one, but it's a one-liner ;-)
function RemoveLeadingZeros(const aValue: String): String;
begin
Result := IntToStr(StrToIntDef(aValue,0));
end;
Only works for numbers within the Integer range, of course.
Searching for this in 2021 there is a much better solution that I found now and that is using the TStringHelper function TrimLeft as follows
myString := myString.TrimLeft(['0']);
see here for more on the documentation http://docwiki.embarcadero.com/Libraries/Sydney/en/System.SysUtils.TStringHelper.TrimLeft
Try this:
function TFrmMain.removeLeadingZeros(const yyy: string): string;
var
xxx : string;
begin
xxx:=yyy;
while LeftStr(xxx,1) = '0' do
begin
Delete(xxx,1,1);
end;
Result:=xxx;
end;
Is it possible to pass a string as a TSysCharSet variable?
This does not compile of course:
var
AValidChars: SysUtils.TSysCharSet;
AResult: string;
begin
// Edit1.Text can contain 0..9 or a..z
AValidChars := SysUtils.TSysCharSet( [Edit1.Text] );
end;
Thanks,
Bill
No, it is not possible to simply pass a string as a TSysCharSet.
What you can do however is to create a TSysCharSet which contains all the chars in the string. This code would do this:
var
AValidChars: SysUtils.TSysCharSet;
s: AnsiString;
i: integer;
begin
// Edit1.Text can contain 0..9 or a..z
AValidChars := [];
s := Edit1.Text;
for i := 1 to Length(s) do
Include(AValidChars, s[i]);
end;
If you are not using an earlier Delphi version you could also make use of "for ... in" instead of the loop above:
var
AValidChars: SysUtils.TSysCharSet;
c: AnsiChar;
begin
// Edit1.Text can contain 0..9 or a..z
AValidChars := [];
for c in Edit1.Text do
Include(AValidChars, c);
end;
Note that in both code snippets AnsiString / AnsiChar is used, as this technique will not work with WideString or the Unicode string type introduced with Delphi 2009.
Many thanks to Craig Stuntz, Ken White and Rob Kennedy for their very valuable comments, I have edited this answer to address all of their points.
If in your Edit1.Text you have the string:
'0..9'
Then the following code should help you:
var
AValidChars: SysUtils.TSysCharSet;
StartChar, EndChar: char;
c: char;
begin
StartChar := Edit1.Text[1]; // some validation should be done
EndChar := Edit1.Text[4];
AValidChars := [];
for c := StartChar to EndChar do
Include(AValidChars, c);
end;
A Delphi/Pascal parser can be used to validate the input.
Update:
More elaborated function supporting set constructors:
function StrToSysCharSet(const S: string): TSysCharSet;
var
Elements: TStringList;
CurrentElement: string;
StartChar, EndChar: char;
c: char;
i: Integer;
p: Integer;
function ReadChar: Char;
begin
Result := CurrentElement[p];
Inc(p);
end;
function NextIsDotDot: Boolean;
begin
Result := '..' = Copy(CurrentElement, p, 2);
end;
begin
Elements := TStringList.Create;
try
Elements.CommaText := S;
Result := [];
for i := 0 to Elements.Count - 1 do
begin
CurrentElement := Trim(Elements[i]);
p := 1;
StartChar := ReadChar;
if NextIsDotDot then
begin
Inc(p, 2);
EndChar := ReadChar;
for c := StartChar to EndChar do
Include(Result, c);
end
else
Include(Result, StartChar);
end;
finally
Elements.Free;
end;
end;
It can be used like this:
S := '0..9, a..z';
AValidChars := StrToSysCharSet(S);
or
S := '0..9 and a..z';
AValidChars := StrToSysCharSet(AnsiReplaceText(S, ' and ', ', '));
Adapting to support
S := '''0''..''9'' and ''a''..''z'''
is simple.