Function exchange(input: String): String; Var I: Integer; Begin Result:=input; I:=Length(Result); For I:=1 To I Do Begin If Result[I]='a' Then Result[I]:='b'; //[DCC Error] test.pas(13): E1047 Unsafe code 'String index to var param' If Result[I]='c' Then Result[I]:='d'; //[DCC Error] test.pas(14): E1047 Unsafe code 'String index to var param' If Result[I]='e' Then Result[I]:='f'; //[DCC Error] test.pas(15): E1047 Unsafe code 'String index to var param' End; End; 

It is to change what warnings can be turned off, I know, I wonder how this “reliable” code is written and what exactly does he dislike about it?

  • one
    What is it like? I:=Length(Result); For I:=1 To I Do Begin I:=Length(Result); For I:=1 To I Do Begin can at least For I:=1 To Length(Result) Do Begin , and how strange it is to see a loop on a variable up to the value of the same variable using a loop on the same variable - LamerXaKer
  • one
    @LamerXaKer, "What is it like?" This is so that at each step of the cycle the length is not re-calculated, a standard trick, so as not to start a new variable, it compiles and works correctly - Isaev
  • one
    I want to draw attention to the fact that after IF and replacements must go continue. If the characters for the replacement coincided with those for what needs to be changed, then there would be no working code. For example, "a" to "o", "o" to "and" - in the end, "a" would change first to "o", and then to "and", well, everything "o" to "and" would change - Albert Fomin
  • one
    And even better not to plant empty talk here, this is a demo example, the answer is received. Interesting was the fact of receiving a warning, you can leave one line) - Isaev
  • one
    In the question sample code illustrating the question. Speculation about what it would be if the grandmother was .... and what's the real code, they are not relevant. - Kromster

2 answers 2

Here such code will be correct and error-free:

 Function exchange(input: String): String; Var I: Integer; sb: TStringBuilder; Begin sb := TStringBuilder.Create(input); try For I := 0 To sb.Length - 1 Do Begin if sb.Chars[I] = 'a' Then sb.Chars[I] := 'b'; if sb.Chars[I] = 'c' Then sb.Chars[I] := 'd'; if sb.Chars[I] = 'e' Then sb.Chars[I] := 'f'; End; Result := sb.ToString; finally sb.Free; end; End; 

And now the explanation:

In modern versions of Delphi, when introducing support for new platforms (iOS, Android, x64, etc.), it was decided to move away from the lines indexed by 1 .. N in favor of the standard ones with indexing 0 .. N-1 . This change breaks virtually all existing code. Therefore, its implementation is very slow and gradual. In some version they added warnings to some {$IFDEF ZEROBASEDSTRINGS} . Somewhere this directive is enabled by default, but mostly not.

What does this warning mean - that it is not safe to access rows by index; Indexing may not be known in advance (depending on the directive and target platform).

How to be?

  • The simplest solution if you only need Win32 is to turn off warnings and continue to live in peace.
  • The best thing is if you need new platforms - rewrite the code using the string TStringBuilder ( TStringBuilder ), which will figure out how the string is indexed and provides a single interface to it through its methods.

PS Oddly enough, For I:=1 To I Do is a completely correct code, since the loop keeps the maximum value before the start of the iteration.

  • one
    here thanks, the kind person, for an available explanation! Otherwise, I still have nothing useful to do but to "turn off warnings from the compiler directive". Now I will write the correct code) - Isaev
  • @Isaev himself discovered a lot of new things while writing the answer, so thank you for the interesting question. And write better as appropriate. For example, I have disabled the warnings and check the indexing myself, because starting a row builder for each sneeze looks a bit expensive. - Kromster
  • one
    This is a habit from the institute, they were taught that the correct code should be compiled without varnungs). Through the builder, it seems to work faster, when adding a character to the end, it does not recreate the string, the memory is also allocated not by element, but in advance by blocks. you can take work through it to class so it doesn't look expensive - Isaev

As mentioned above, the varning comes from the fact that modern versions of Delphi began to support various platforms, on which line numbering was done from 0 , and not from 1 , as it was before on Win32. And now, in order for the code to work on all platforms, the canonical way to iterate over rows is to use the Low and High functions:

 function Exchange(const AInput: string): string; var I: Integer; begin Result := AInput; for I := Low(Result) to High(Result) do begin if Result[I] = 'a' then Result[I] := 'b' else if Result[I] = 'c' then Result[I] := 'd' else if Result[I] = 'e' then Result[I] := 'f'; end; end; 

However, this method will work only in Delphi XE3 and above, but in XE2 and below, there will be errors:

 E2198 Low cannot be applied to a long string E2198 High cannot be applied to a long string 

And if you suddenly want to support the old versions of Delphi, you will have to write your wrappers over these functions and use them:

 function StrLow(const S: string): Integer; inline; begin Result := {$IFDEF XE3UP} Low(S) {$ELSE} 1 {$ENDIF}; end; function StrHigh(const S: string): Integer; inline; begin Result := {$IFDEF XE3UP} High(S) {$ELSE} Length(S) {$ENDIF}; end; 

However, there is also a pitfall here - if you suddenly want to temporarily enable / disable support for new lines in a ZEROBASEDSTRINGS through the ZEROBASEDSTRINGS directive and call one of these functions, you will get an error, as they will produce the result regardless of this setting (the global settings of this directive will be used). Therefore, it is simply not necessary to manage the hands of this directive within its functions / units.

For a detailed discussion of the question of writing a backward-compatible code, see here: How to work with 0-based strings in Delphi XE5?

  • Thank you for reminding me of my alternative solution and additional pitfalls! =) - Kromster