Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi - Loop through the String

I'm trying to find out if String is "mnemonic type"... My mnemonic type consists of letters from 'a' to 'z' and from 'A' to 'Z', digits from '0' to '9', and additionaly '_'. I build code like below. It should result with True if given string match my mnemonic pattern otherwise False:

 TRes := True;
 for I := 0 to (AString.Length - 1) do
 begin
     if not ((('0' <= AString[I]) and (AString[I] <= '9')) 
       or (('a' <= AString[I]) and (AString[I] <= 'z')) 
       or (('A' <= AString[I]) and (AString[I] <= 'Z')) 
       or (AString[I] = '_')) then
         TRes := False;
 end;

This code always results with False.

like image 462
Sebastian Xawery Wiśniowiecki Avatar asked Dec 01 '22 02:12

Sebastian Xawery Wiśniowiecki


2 Answers

I'm assuming that since you tagged the question XE5, and used zero-based indexing, that your strings are zero-based. But perhaps that assumptions was mistaken.

Your logic is fine, although it is rather hard to read. The code in the question is already doing what you intend. At least the if statement does indeed perform the test that you intend.

Let's just re-write your code to make it easier to understand. I'm going to lay it our differently, and use a local loop variable to represent each character:

for C in AString do
begin
  if not (
        (('0' <= C) and (C <= '9'))  // C is in range 0..9
     or (('a' <= C) and (C <= 'z'))  // C is in range a..z
     or (('A' <= C) and (C <= 'Z'))  // C is in range A..Z
     or (C = '_')                    // C is _
  ) then
    TRes := False;
end;

When written like that I'm sure that you will agree that it performs the test that you intend.

To make the code easier to understand however, I would write an IsValidIdentifierChar function:

function IsValidIdentifierChar(C: Char): Boolean;
begin
  Result :=  ((C >= '0') and (C <= '9'))
          or ((C >= 'A') and (C <= 'Z'))
          or ((C >= 'a') and (C <= 'z'))
          or (C = '_');
end;

As @TLama says, you can write IsValidIdentifierChar more concisely using CharInSet:

function IsValidIdentifierChar(C: Char): Boolean;
begin
  Result := CharInSet(C, ['0'..'9', 'a'..'z', 'A'..'Z', '_']);
end;

Then you can build your loop on top of this function:

TRes := True;
for C in AString do
  if not IsValidIdentifierChar(C) do 
  begin
    TRes := False;
    break;
  end;
like image 103
David Heffernan Avatar answered Dec 24 '22 05:12

David Heffernan


String type is 1-based. dynamic Arrays are 0-based. Better use for ... in so you are safe for future Delphi's.

Testing for ranges of possible character values can be done more efficiently (and more conciece) is CharInSet.

function IsMnemonic( AString: string ): Boolean;
var
  Ch: Char;
begin
  for Ch in AString do 
    if not CharInSet( Ch, [ '_', '0'..'9', 'A'..'Z', 'a'..'z' ] ) then 
      Exit( False );
  Result := True;
end;
like image 32
Ritsaert Hornstra Avatar answered Dec 24 '22 04:12

Ritsaert Hornstra