6

我通过在运行时将所有字符串放在 TStringList 中来在应用程序中实现语言翻译:

procedure PopulateStringList;
begin  
  EnglishStringList.Append('CAN_T_FIND_FILE=It is not possible to find the file');
  EnglishStringList.Append('DUMMY=Just a dummy record');
  // total of 2000 record appended in the same way
  EnglishStringList.Sorted := True; // Updated comment: this is USELESS!
end;

然后我使用以下方法获得翻译:

function GetTranslation(ResStr:String):String;
var
  iIndex : Integer;
begin
  iIndex := -1;
  iIndex :=  EnglishStringList.IndexOfName(ResStr);
  if iIndex >= 0 then
  Result :=  EnglishStringList.ValueFromIndex[iIndex] else
  Result := ResStr + ' (Translation N/A)';
end;

无论如何,使用这种方法大约需要 30 微秒来定位记录,有没有更好的方法来实现相同的结果?

更新:为了将来参考,我在这里写了按照建议使用 TDictionary 的新实现(适用于 Delphi 2009 和更新版本)

procedure PopulateStringList;
begin  
  EnglishDictionary := TDictionary<String, String>.Create;
  EnglishDictionary.Add('CAN_T_FIND_FILE','It is not possible to find the file');
  EnglishDictionary.Add('DUMMY','Just a dummy record');
  // total of 2000 record appended in the same way
end;


function GetTranslation(ResStr:String):String;
var
  ValueFound: Boolean;
begin
  ValueFound:=  EnglishDictionary.TryGetValue(ResStr, Result);
  if not ValueFound then Result := Result + '(Trans N/A)';
end;

新的 GetTranslation 函数的执行速度(在我的 2000 个样本记录上)比第一个版本快 1000 倍。

4

5 回答 5

18

THashedStringList应该更好,我想。

于 2010-09-14T14:28:11.953 回答
15

在 Delphi 2009 或更高版本中,我会在 Generics.Collections 中使用 TDictionary<string,string>。另请注意,有用于翻译应用程序的免费工具,例如http://dxgettext.po.dk/ 。

于 2010-09-14T17:30:05.373 回答
14

如果 THashedStringList 适合您,那就太好了。它最大的弱点是每次更改列表的内容时,都会重新构建 Hash 表。因此,只要您的列表很小或不经常更改,它就会对您有用。

有关这方面的更多信息,请参阅:THashedStringList 弱点,它提供了一些替代方案。

如果您有一个可能会更新的大列表,您可能想通过gabr尝试GpStringHash,这样就不必在每次更改时重新计算整个表。

于 2010-09-14T16:06:00.643 回答
4

我认为您没有正确使用 EnglishStringList(TStringList) 。这是一个排序列表,您添加元素(字符串),然后对其进行排序,但是当您搜索时,您通过部分字符串(只有名称,带有IndexOfName)来执行此操作。

如果在排序列表中使用IndexOfName ,则TStringList不能使用双分搜索。它使用顺序搜索。

(这是 IndexOfName 的实现)

  for Result := 0 to GetCount - 1 do
  begin
    S := Get(Result);
    P := AnsiPos('=', S);
    if (P <> 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then Exit;
  end;

我认为这是表现不佳的原因。
另一种方法是使用 2 TStringList:
* 第一个(已排序)仅包含“名称”和指向包含该值的第二个列表的指针;您可以使用 Object 属性的“指针”实现此指向第二个列表的指针。
* 第二个(未排序的)列表包含值。

当你搜索时,你会在第一个列表中搜索;在这种情况下,您可以使用Find方法。当您找到名称时,指针(使用 Object 属性实现)为您提供第二个列表中的位置和值。

在这种情况下,Sorted List 上的 Find 方法比 HashList 更有效(必须执行一个函数来获取一个值的位置)。

问候。

Pd:请原谅我的英语错误。

于 2010-09-14T16:18:13.423 回答
2

您还可以使用 CLASS HELPER 重新编程“IndexOfName”函数:

TYPE
  TStringsHelper = CLASS HELPER FOR TStrings
                     FUNCTION IndexOfName(CONST Name : STRING) : INTEGER;
                   END;

FUNCTION TStringsHelper.IndexOfName(CONST Name : STRING) : INTEGER;
  VAR
    SL  : TStringList ABSOLUTE Self;
    S,T : STRING;
    I   : INTEGER;

  BEGIN
    IF (Self IS TStringList) AND SL.Sorted THEN BEGIN
      S:=Name+NameValueSeparator;
      IF SL.Find(S,I) THEN
        Result:=I
      ELSE IF (I<0) OR (I>=Count) THEN
        Result:=-1
      ELSE BEGIN
        T:=SL[I];
        IF CompareStrings(COPY(T,1,LENGTH(S)),S)=0 THEN Result:=I ELSE Result:=-1
      END;
      EXIT
    END;
    Result:=INHERITED IndexOfName(Name)
  END;

(如果您不喜欢 CLASS HELPER 或在您的 Delphi 版本中没有它们,或者在后代 TStrings 类中实现它)。

这将在排序的 TStringList 上使用二进制搜索,并在其他 TStrings 类上使用顺序搜索。

于 2010-10-08T12:48:31.050 回答