Bookstore

Recent

Author Topic: [SOLVED] IsNullOrWhiteSpace()  (Read 1004 times)

BeniBela

  • Hero Member
  • *****
  • Posts: 714
    • homepage
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #15 on: March 25, 2020, 12:23:49 am »
Recently I tried to use this function too.

Why is that such a stupid function?



Code: Pascal  [Select]
  1. If ansistring.IsNullOrWhiteSpace(Expression) = False Then


That is so ugly. Why not make it a non-class function in the helper and do:

Code: Pascal  [Select]
  1. If Expression.IsNullOrWhiteSpace = False Then


Code: Pascal  [Select]
  1. class function TStringHelper.IsNullOrWhiteSpace(const AValue: string): Boolean;
  2. begin
  3.   Result:=system.Length(SysUtils.Trim(AValue))=0;
  4. end;

A temporary string?? Never create a temporary string to check if the string contains something

jamie

  • Hero Member
  • *****
  • Posts: 2459
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #16 on: March 25, 2020, 02:12:40 am »
Reading the MS docs from what I understand of it, the intention is to search the string for any occurrence of a non nil and non white space character. If found then the statement is false..

 For it to return True, the string must be empty or contain one or more WhiteSpace characters in the string without any other chars. This would indicate ether empty string ether way I guess.

 Depending on how much CPU time you want to chew up, trimming it involves string truncation and length checks. But in the end if the string is empty when done it is considered a TRUE result.

 But the Trim does not take into account of the non-Breakable WhiteSpace so the way I did was not just for a WhiteSpace but for both.

 Doing both WhiteSpaces may not be desirable so I wouldn't suggest the current version to do that but it could serve as a secondary version to take care of both and the way I did it I believe saves CPU time and code space.
   I suppose I could of done a direct indexing of the string instead of using the enumerator, I don't know if there is any advantage to processing time but it makes it look more organized.  :)



 

Number 1 at blue screen app creations!

440bx

  • Hero Member
  • *****
  • Posts: 1558
Re: IsNullOrWhiteSpace()
« Reply #17 on: March 25, 2020, 02:35:46 am »
@PascalDragon

Quote
As a general remark: If you have an expression returning Boolean (like IsNullOrWhiteSpace does), then you should not use = False (or = True). Use the following instead:

Code: Pascal  [Select]
if not AnsiString.IsNullOrWhiteSpace(Expression) then ...
// or (for true)
if AnsiString.IsNullOrWhiteSpace(Expression) then ...
What is your particular reasoning for this?

I prefer my verbosity. It leads to less buggy code.
Maybe PascalDragon didn't see your question so I will answer it. 

You claim that the verbosity in this case leads to less buggy code but, actually, in this particular case, it is opening the door to buggy code that the construction PascalDragon suggested, leaves closed (as it should be.)

Here is an example of how your statement invites bugs
Code: Pascal  [Select]
  1. var a : Boolean = true;
  2. { case 1 } if a then writeln('programming is fun');   // but the way you are doing it could lead to...
  3.  
  4. { case 2 } if a = false then writeln('programming is fun');
In the first case, the truth value of the if expression only depends on the value of "a".  In the second case, the truth value of the if expression depends on the comparison you're making.  Therefore, in the second case, you've opened the door to a bug by mistakenly comparing against the wrong value. 

You just made me think about something.  There are some people who talk too much and, apparently, there are programmers who code too much. ;)

HTH.

using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

GypsyPrince

  • Jr. Member
  • **
  • Posts: 80
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #18 on: March 25, 2020, 07:19:58 am »
@440bx

Quote
Here is an example of how your statement invites bugs
Code: Pascal  [Select]
var a : Boolean = true;
{ case 1 } if a then writeln('programming is fun');   // but the way you are doing it could lead to...
 
{ case 2 } if a = false then writeln('programming is fun');
In the first case, the truth value of the if expression only depends on the value of "a".  In the second case, the truth value of the if expression depends on the comparison you're making.  Therefore, in the second case, you've opened the door to a bug by mistakenly comparing against the wrong value.

You actually just re-enforced my point about verbosity for me.  I do not want the statement "writeln('programming is fun');" to execute unless 'a' is specifically equal to false.  If 'a' is anything other than "false", I do not want the statement to execute.

Your response makes me think of something, too: there are some programmers who code too much, but there are WAY too many programmers who produce buggy code because they take shortcuts.

@jamie

I agree with you completely, which is why I chose to stick with my own function.

Mine tests for, and returns True if the string is equal to {Alt+0160} and Chr(160), but False if it is equal to {Alt+160}.  Note that {Alt+0160} and {Alt+160} produce 2 different characters.  It also returns True if the string is equal to {Alt+255}, but returns False if it is equal to {Alt+0255} or Chr(255).

The IsNullOrWhiteSpace() function does not test for these breaking spaces.  The reason I check for those two breaking spaces is because I often use those two characters.
May the (Mass × Acceleration) be with you!

PascalDragon

  • Hero Member
  • *****
  • Posts: 1088
  • Compiler Developer
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #19 on: March 25, 2020, 10:03:59 am »
Why is that such a stupid function?



Code: Pascal  [Select]
  1. If ansistring.IsNullOrWhiteSpace(Expression) = False Then


That is so ugly. Why not make it a non-class function in the helper and do:

Code: Pascal  [Select]
  1. If Expression.IsNullOrWhiteSpace = False Then

The answer for this is simple: Delphi compatibililty. Though in theory one could make an overload that works on the string value instead...

@PascalDragon

Quote
As a general remark: If you have an expression returning Boolean (like IsNullOrWhiteSpace does), then you should not use = False (or = True). Use the following instead:

Code: Pascal  [Select]
if not AnsiString.IsNullOrWhiteSpace(Expression) then ...
// or (for true)
if AnsiString.IsNullOrWhiteSpace(Expression) then ...
What is your particular reasoning for this?

I prefer my verbosity. It leads to less buggy code.
Maybe PascalDragon didn't see your question so I will answer it. 

I'm not checking the forum all day long. My usual time is in the morning (Central European Time) and then only once for unread messages. ;D

And @GypsyPrince, 440box is right when he says that this closes the door for potential bugs. For this you need to understand how the Boolean types work: From a programmer's point of view they only have the values True and False, however internally that isn't entirely correct. They aren't just 0 and 1 (except inside bitpacked structures), but instead they have a size of at least 1 Byte. Values of the types Boolean, Boolean8, Boolean16, Boolean32 and Boolean64 are considered False if their value is 0 and True if the value is 1. Values of the types ByteBool, WordBool, LongWord and QWordBool are considered False if their value is 0, but the True if their value is <> 0. If you do a comparison with = False you do a comparison with the value of = False, which is 0. And for True the comparison is with 1. You can maybe already see where I'm going with that?

Now assume you have a bug in your code (or in third party code) that returns a "corrupted" Boolean value (e.g. replaced with some random value where the lowest bit is not set). Then this can mean that the = True check will not be handled correctly.

Please note that in most cases the compiler correctly handles such comparisons by reducing them from BoolExpr = True|False to BoolExpr, but that is no guarantee (especially in more complex expressions).

Also it's simply more correct to use the Boolean expression directly: the if-statements takes a Boolean expression. Any expression that returns a Boolean is automatically a Boolean expression. So why complicate things further by introducing another Boolean expression?

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 739
    • Lebeau Software
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #20 on: March 25, 2020, 07:54:28 pm »
Here's a more corrected code view to what MS states, this one handles the Non-Break and does the overload.
Code: Pascal  [Select]
  1. Function IsNullOrBothWhiteSpace(Const S:String):Boolean;
  2. Var
  3.   C:char;
  4. begin
  5.  result  := True;
  6.   For C in S do if Not ((Ord(C) and $7F)=32) Then result := False;
  7. end;
  8. Function IsNullOrBothWhiteSpace(const S:Pchar):Boolean;
  9. Begin
  10.  Result := IsNullOrBothWhiteSpace(String(S));
  11. end;                                          
  12.  

I would opt for the other way around - make the PChar version loop, and have the String version call the PChar version.  Avoids a memory allocation that way.  You should not have to allocate memory just to iterate through it.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

GypsyPrince

  • Jr. Member
  • **
  • Posts: 80
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #21 on: March 25, 2020, 10:01:18 pm »
Yeah, I'm sticking by what I said earlier.
May the (Mass × Acceleration) be with you!

jamie

  • Hero Member
  • *****
  • Posts: 2459
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #22 on: March 25, 2020, 10:06:16 pm »
You mean this..

Code: Pascal  [Select]
  1. Function IsNullOrBothWhiteSpace(S:Pchar):Boolean; //Pointer should be on stack so reuse it.
  2. Begin
  3.  result  := True;
  4.  If S <> Nil Then
  5.  While (Result)And(S^<>#0)Do
  6.    Begin
  7.      Result :=(PByte(S)^ and $7F)=32;
  8.      Inc(S);
  9.    end;
  10. end;
  11. Function IsNullOrBothWhiteSpace(Const S:String):Boolean;
  12. begin
  13.  Result := IsNullOrBothWhiteSpace(Pchar(S));
  14. end;                                    
  15.  

 This version returns when it finds the first non white space in the list so that it does not waste time looking at the rest of it needlessly .

Number 1 at blue screen app creations!

GypsyPrince

  • Jr. Member
  • **
  • Posts: 80
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #23 on: March 25, 2020, 10:15:43 pm »
@jamie

Good stuff! Short-circuit that thing!!  8)
May the (Mass × Acceleration) be with you!

jamie

  • Hero Member
  • *****
  • Posts: 2459
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #24 on: March 25, 2020, 11:13:23 pm »
Short circuit is good as long as you don't let the magic blue smoke out!  :D
Number 1 at blue screen app creations!

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 8052
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #25 on: March 25, 2020, 11:24:02 pm »
I would opt for the other way around - make the PChar version loop, and have the String version call the PChar version.

Typecasting a string to a pchar only supports a subset of string, namely the strings that don't contain #0.

I don't know if it is bad in this case, but in general it is bad advise.

jamie

  • Hero Member
  • *****
  • Posts: 2459
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #26 on: March 25, 2020, 11:32:55 pm »
I don't know but from what I've been told there managed strings seem to let anything in them, I call that managed memory blocks not strings but who am I..

 I this case I don't think there is any issues since even for managed strings they are terminated via a #0.
Number 1 at blue screen app creations!

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 739
    • Lebeau Software
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #27 on: March 25, 2020, 11:45:37 pm »
You mean this..

Something like that.  Though, as I think more about it, it would be better to allow iterating a String to uses its length, and iterating a PChar to use its null terminator, eg:

Code: Pascal  [Select]
  1. function IsNullOrBothWhiteSpace(S: PChar; Len: Integer): Boolean;
  2. begin
  3.   if S <> nil then
  4.   begin
  5.     if Len > 0 then
  6.     begin
  7.       repeat
  8.         if (PByte(S)^ and $7F) <> 32 then Exit(False);
  9.         Inc(S);
  10.         Dec(Len);
  11.       until Len = 0;
  12.     end
  13.     else if Len < 0 then
  14.     begin
  15.       while S^ <> #0 do
  16.       begin
  17.         if (PByte(S)^ and $7F) <> 32 then Exit(False);
  18.         Inc(S);
  19.       end;
  20.     end
  21.   end;
  22.   Exit(True);
  23. end;
  24.  
  25. function IsNullOrBothWhiteSpace(S: PChar): Boolean;
  26. begin
  27.   Result := IsNullOrBothWhiteSpace(S, -1);
  28. end;
  29.  
  30. function IsNullOrBothWhiteSpace(const S: String): Boolean;
  31. begin
  32.   Result := IsNullOrBothWhiteSpace(PChar(S), Length(S));
  33. end;                                    
  34.  

Or simply:

Code: Pascal  [Select]
  1. function IsNullOrBothWhiteSpace(S: PChar): Boolean;
  2. begin
  3.   if (S <> nil) and (S^ <> #0) then
  4.   begin
  5.     repeat
  6.       if (PByte(S)^ and $7F) <> 32 then Exit(False);
  7.       Inc(S);
  8.     until S^ = #0;
  9.   end;
  10.   Exit(True);
  11. end;
  12.  
  13. function IsNullOrBothWhiteSpace(const S: String): Boolean;
  14. var
  15.   P: PChar;
  16.   Len: Integer;
  17. begin
  18.   Len := Length(S);
  19.   if Len > 0 then
  20.   begin
  21.     P := PChar(S);
  22.     repeat
  23.       if (PByte(P)^ and $7F) <> 32 then Exit(False);
  24.       Inc(P);
  25.       Dec(Len);
  26.     until Len = 0;
  27.   end;
  28.   Exit(True);
  29. end;                                    
  30.  
« Last Edit: March 25, 2020, 11:47:21 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 739
    • Lebeau Software
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #28 on: March 25, 2020, 11:46:30 pm »
Typecasting a string to a pchar only supports a subset of string, namely the strings that don't contain #0.

Only if you don't take the string's length into account while iterating its Char data.
« Last Edit: March 25, 2020, 11:48:58 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

PascalDragon

  • Hero Member
  • *****
  • Posts: 1088
  • Compiler Developer
Re: [SOLVED] IsNullOrWhiteSpace()
« Reply #29 on: March 26, 2020, 09:26:17 am »
I don't know but from what I've been told there managed strings seem to let anything in them, I call that managed memory blocks not strings but who am I..

 I this case I don't think there is any issues since even for managed strings they are terminated via a #0.

Yes, they are terminated with #0, but Pascal strings can also contain #0 inside the string:

Code: Pascal  [Select]
  1. MyString := 'Hello'#0'World';
  2. Writeln(Length(MyString));
  3. Writeln(StrLen(PChar(MyString));

The first will write 11, the second will write 5.