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時間くらいはまりました。皆さんご注意を。