Delphi XE2のVCL、AnsiStringsユニットで定義されているAnsiPos関数がおかしな動作をするのを発見したので、お知らせいたします。同名の関数はSysUtilsとAnsiStringsの2ユニットで定義されており、それぞれUnicodeString型/AnsiString型の文字列を扱う関数が実装されていますが、そのうちAnsiString型を扱う方だけが変な動作をします。具体的には、文字列の途中にヌル文字(#$00)があると、その後にある部分文字列の検索ができなくなります。

 

■再現コード
procedure TMainForm.Button1Click(Sender:TObject);
  var Str:string;
      AnsiStr:AnsiString;
begin
  Str    :='0123456789ABCDEF';
  AnsiStr:='0123456789ABCDEF';
  WriteLn(Str);
  WriteLn(SysUtils   .AnsiPos('78',Str    ));
  WriteLn(AnsiStrings.AnsiPos('78',AnsiStr));
  WriteLn(SysUtils   .AnsiPos('BC',Str    ));
  WriteLn(AnsiStrings.AnsiPos('BC',AnsiStr));
  WriteLn;
  Str    [10]:=#$0000;
  AnsiStr[10]:=#$00;
  WriteLn(Str);
  WriteLn(SysUtils   .AnsiPos('78',Str    ));
  WriteLn(AnsiStrings.AnsiPos('78',AnsiStr));
  WriteLn(SysUtils   .AnsiPos('BC',Str    ));
  WriteLn(AnsiStrings.AnsiPos('BC',AnsiStr));
end;

■実行結果

0123456789ABCDEF
8
8
12
12

012345678 ABCDEF  <======= 文字'9'をヌル文字に変えた。 
8
8
12
0  <======= ヌル文字の後にある部分文字列'BC'が探せなくなった。

もしSubStr引数にヌル文字が含まれていないのであれば、事前にS引数に含まれるヌル文字をすべて他の文字(SubStr引数にも含まれていない文字)に置換してから本来のAnsiPos関数に渡す方法により、上記の問題を回避することができます。次のような代替関数を作っておくと便利です。

function AnsiPosAlt(SubStr,S:AnsiString):LongInt;
  var CharIndex:LongInt;
begin
  for CharIndex:=1 to Length(S) do
      if S[CharIndex]=#$00 then
          S[CharIndex]:=#$FF;  {<======= SubStr引数に文字#$FFが含まれていない前提です }
  Result:=AnsiStrings.AnsiPos(SubStr,S);
end;

 

私自身は4時間くらいはまりました。皆さんご注意を。