在Delphi中,是否有必要将字符串转换为WideString?

21
我发现了一个Windows API函数,可以执行字符串的“自然比较”。它的定义如下:
int StrCmpLogicalW(
    LPCWSTR psz1,
    LPCWSTR psz2
);

为了在Delphi中使用它,我这样声明它:
interface
  function StrCmpLogicalW(psz1, psz2: PWideChar): integer; stdcall;

implementation
  function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';

因为它比较的是Unicode字符串,所以当我想要比较ANSI字符串时,我不确定该如何调用它。看起来将字符串转换为WideString然后再转换为PWideChar就足够了,但我不知道这种方法是否正确:

function AnsiNaturalCompareText(const S1, S2: string): integer;
begin
  Result := StrCmpLogicalW(PWideChar(WideString(S1)), PWideChar(WideString(S2)));
end;

我对字符编码知之甚少,这就是我提问的原因。这个函数是否可行?或者我应该先以某种方式转换比较的字符串?

4个回答

11

请记住,将字符串转换为WideString时,将使用默认的系统代码页进行转换,这可能不是您所需的。通常,您需要使用当前用户的区域设置。

来自System.pas中的WCharFromChar

Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
  WCharDest, DestChars);

您可以通过调用SetMultiByteConversionCodePage来更改DefaultSystemCodePage。


说实话,我想创建一个函数,它将是 AnsiCompareText() 的对应函数(当然,这个函数执行 ANSI 字符串的词法比较)。它使用当前用户的区域设置还是默认系统代码页?谢谢。 - Mariusz Schimke
使用源代码: Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1), Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL;从MSDN中得知:LOCALE_USER_DEFAULT 当前用户的默认区域设置。因此,它不使用当前区域设置,而是使用当前用户的默认区域设置。这可能是你真正想要的,也可能不是。 :-/ - gabr

5
更简单的完成任务的方法是将您的函数声明为:
interface
   function StrCmpLogicalW(const sz1, sz2: WideString): Integer; stdcall;

implementation
   function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';

因为WideString变量实际上是指向WideChar的指针(与AnsiString变量指向AnsiChar的方式相同)。
这样,Delphi会自动将AnsiString“升级”为WideString

更新

既然我们现在处于UnicodeString世界中,你可以将其写成:
interface
   function StrCmpLogicalW(const sz1, sz2: UnicodeString): Integer; stdcall;

implementation
   function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';

由于UnicodeString变量仍然是指向以WideChars结尾的\0\0字符串的指针。因此,如果您调用:
var
    s1, s1: AnsiString;
begin
    s1 := 'Hello';
    s2 := 'world';

    nCompare := StrCmpLogicalW(s1, s2);
end;

当您尝试将AnsiString传递给需要UnicodeString的函数时,编译器会自动在生成的代码中调用MultiByteToWideChar

CompareString在Windows 7中支持数字排序

从Windows 7开始,微软在CompareString中添加了SORT_DIGITSASNUMBERS

Windows 7:在排序期间将数字视为数字,例如将“2”排在“10”之前。

这些都没有帮助回答实际问题,该问题涉及何时必须转换或强制转换字符串。

3

任何Windows API的A版本都会为您将"AnsiString"转换为"WideString",然后调用"宽字符"API。作者本应该只需执行相同的MultiByteToWideChar。但在Delphi中,将string强制转换为WideString并让编译器为您调用它就足够简单了。 - Ian Boyd

2

使用System.StringToOleStr,它是一个方便的MultiByteToWideChar包装器,请参见Gabr的答案

function AnsiNaturalCompareText(const S1, S2: string): integer;   
var
  W1: PWideChar;
  W2: PWideChar;
begin
  W1 := StringToOleStr(S1);
  W2 := StringToOleStr(S2);
  Result := StrCmpLogicalW(W1, W2);
  SysFreeString(W1);
  SysFreeString(W2);
end;

但是,Ian Boyd的解决方案看起来更美观、更好用!


1
请注意,您刚刚泄漏了两个宽字符串。StringToOleStr返回一个PWideChar,您需要使用CoTaskMemFree(或其旧版 道德等效物)进行释放。 - Ian Boyd
1
哦,还有一个人(有足够耐心创建BDN登录)应该编辑doc wiki条目StringToOleStr,以便说出类似“备注:您可以使用SysFreeString释放使用StringToOleStr创建的字符串”。这样文档就记录了正确使用API的方法。 - Ian Boyd
1
抱歉,我是否漏掉了什么?W1和W2变量不应该被传递到StrCmpLogicalW函数中吗,而不是在函数调用中重复字符串转换? - Neville Cook

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接