我找到了一个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;
我对字符编码知之甚少,所以这就是我提问的原因。这个函数是OK还是我应该先以某种方式转换两个比较的字符串?
答案 0 :(得分:11)
请记住,将字符串转换为WideString会使用默认系统代码页转换它,这可能是您需要的,也可能不是。通常,您需要使用当前用户的区域设置。
来自System.pas中的WCharFromChar
:
Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
WCharDest, DestChars);
您可以通过调用SetMultiByteConversionCodePage来更改DefaultSystemCodePage。
答案 1 :(得分: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
变量仍然是指向\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:在排序过程中将数字视为数字,例如,在“10”之前排序“2”。
这些都无法解答实际问题,该问题涉及何时必须转换或投射字符串。
答案 2 :(得分:3)
您的函数可能有一个ANSI变体(我没有检查过)。大多数Wide API也可以作为ANSI版本使用,只需将W后缀更改为A,即可设置。在这种情况下,Windows会为您进行交互式转换。
PS:这是一篇描述缺少StrCmpLogicalA的文章:http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogicala.aspx
答案 3 :(得分:2)
使用System.StringToOleStr
,这是MultiByteToWideChar
周围的便捷包装,请参阅Gabr's answer:
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's solution看起来更好!