我发现了一个 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;
我对字符编码知之甚少,所以这就是我问题的原因。这个函数可以吗?或者我应该首先以某种方式转换两个比较的字符串?
请记住,将字符串转换为 WideString 将使用默认的系统代码页对其进行转换,这可能是也可能不是您需要的。通常,您希望使用当前用户的区域设置。
来自 System.pas 中的
WCharFromChar
:
Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
WCharDest, DestChars);
您可以通过调用 SetMultiByteConversionCodePage 来更改 DefaultSystemCodePage。
完成任务的更简单方法是将函数声明为:
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
变量仍然是指向 \0\0
结尾的 WideChars
字符串的指针。所以如果你打电话:
var
s1, s1: AnsiString;
begin
s1 := 'Hello';
s2 := 'world';
nCompare := StrCmpLogicalW(s1, s2);
end;
当您尝试将
AnsiString
传递给采用 UnicodeString
的函数时,编译器将在生成的代码中自动为您调用 MultiByteToWideChar
。
从 Windows 7 开始,Microsoft 将
SORT_DIGITSASNUMBERS
添加到 CompareString
:
Windows 7: 排序时将数字视为数字,例如将“2”排在“10”之前。
这些都不能帮助回答实际问题,该问题涉及何时必须转换或转换字符串。
您的函数可能有一个 ANSI 变体(我没有检查过)。大多数 Wide API 也提供 ANSI 版本,只需将 W 后缀更改为 A,即可完成设置。在这种情况下,Windows 会透明地为您进行来回转换。
PS:这里有一篇文章描述了 StrCmpLogicalA 的缺失:http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogica.aspx
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 的解决方案 看起来而且更好!
我认为如果你这样做的话,内存管理器会最好地处理它:
function AnsiNaturalCompareText(const S1, S2: string): integer;
begin
Result := StrCmpLogicalW(PWideChar(S1), PWideChar(S2));
end;