“Type mismatch” error on “set of char” in Pascal S

2019-06-02 02:44发布

问题:

I have an working Pascal scripts for Inno Setup, but recently we ran into an issue with Unicode characters. And after changing the Inno Setup to Unicode version we got errors in existing scripts. And this error is too generic - "Type mismatch" for certain line. Since our main script is composed of lots of other scripts that are included I'm not sure if that line is the correct, and it points to this function below, and line with Set of Char. After looking the Inno Setup documentation I found that Unicode version is bit more strict about some things, and also there was some changes around ANSI string handling (and probably Char as well).

function IsValidName(Name: String):String;
var
  Valid_Ch: Set of Char;
  Location: Integer;
begin
    Valid_Ch := ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_', '-'];
    Result := '';
    for Location := 1 to Length(Name) do
    begin
        if Name[Location] in Valid_Ch then
        begin
            Result := Result + Name[Location];
        end
        else
        begin
            Result := Result + '_';
        end;
    end;    
end;

This all looks fine to me, but this is my first touch with Pascal. I would be thankful if someone more experienced could help me about this. Thanks!

回答1:

In Pascal (Script) the set can contain 256 (2^8) values only. In Unicode Inno Setup the Char is 2-byte, hence it can contain a way more than 256 different values. So you cannot use it with set anymore.

You have these options:

  • In your case, as the set is in fact constant, you can use a constant set in the if expression. Interestingly, that works even in Unicode version (possibly there's some hack built in that allows this exception).

    function IsValidName(Name: string): string;
    var
      Location: Integer;
    begin
      Result := Name;
      for Location := 1 to Length(Name) do
      begin
        if not (Name[Location] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_', '-']) then
        begin
          Result[Location] := '_';
        end;
      end;    
    end;
    

Had you needed the set to be variable (not your case):

  • Use Byte and cast between Char and Byte and explicitly replace all characters with Ord() > 255
  • Or use a string instead of the set. For an example, see below.

    function IsValidName(Name: string): string;
    var
      Valid_Ch: string;
      Location: Integer;
    begin
      Valid_Ch := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-';
      Result := Name;
      for Location := 1 to Length(Result) do
      begin
        if Pos(Result[Location], Valid_Ch) = 0 then
        begin
          Result[Location] := '_';
        end;
      end;    
    end;
    

Also note that appending the string char-by-char can be pretty inefficient. Replace the characters in-place instead (as shown in both examples above).