I found a Windows API function that performs \"natural comparison\" of strings. It is defined as follows:
int StrCmpLogicalW(
LPCWSTR psz1,
LPCWSTR p
Keep in mind that casting a string to a WideString will convert it using default system codepage which may or may not be what you need. Typically, you'd want to use current user's locale.
From WCharFromChar
in System.pas:
Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
WCharDest, DestChars);
You can change DefaultSystemCodePage by calling SetMultiByteConversionCodePage.
The easier way to accomplish the task would be to declare your function as:
interface
function StrCmpLogicalW(const sz1, sz2: WideString): Integer; stdcall;
implementation
function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';
Because a WideString
variable is a pointer to a WideChar
(in the same way an AnsiString
variable is a pointer to an AnsiChar
.)
And this way Delphi will automatically "up-convert" an AnsiString to a WideString
for you.
And since we're now in the world of UnicodeString
, you would make it:
interface
function StrCmpLogicalW(const sz1, sz2: UnicodeString): Integer; stdcall;
implementation
function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';
Because a UnicodeString
variable is still a pointer to a \0\0
terminated string of WideChars
. So if you call:
var
s1, s1: AnsiString;
begin
s1 := 'Hello';
s2 := 'world';
nCompare := StrCmpLogicalW(s1, s2);
end;
When you try to pass an AnsiString
into a function that takes a UnicodeString
, the compiler will automatically call MultiByteToWideChar
for you in the generated code.
Starting in Windows 7, Microsoft added SORT_DIGITSASNUMBERS
to CompareString:
Windows 7: Treat digits as numbers during sorting, for example, sort "2" before "10".
None of this helps answer the actual question, which deals with when you have to convert or cast strings.
There might be an ANSI variant for your function to (I haven't checked). Most Wide API's are available as an ANSI version too, just change the W suffix to an A, and you're set. Windows does the back-and-forth conversion transparantly for you in that case.
PS: Here's an article describing the lack of StrCmpLogicalA : http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogicala.aspx
Use System.StringToOleStr, which is a handy wrapper around MultiByteToWideChar
, see 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;
But then, Ian Boyd's solution looks and is much nicer!