Нужно ли преобразовать строку в WideString в Delphi?
Я нашел функцию 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;
Я очень мало знаю о кодировке символов, так что это причина моего вопроса. Является ли эта функция ОК или мне нужно сначала преобразовать обе сравниваемые строки?
Ответы
Ответ 1
Имейте в виду, что приведение строки в WideString будет конвертировать ее с использованием системной кодовой страницы по умолчанию, которая может быть или не быть тем, что вам нужно. Как правило, вы хотите использовать текущий язык пользователя.
От WCharFromChar
в System.pas:
Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
WCharDest, DestChars);
Вы можете изменить DefaultSystemCodePage, вызывая SetMultiByteConversionCodePage.
Ответ 2
Более простой способ выполнить задачу состоит в том, чтобы объявить вашу функцию как:
interface
function StrCmpLogicalW(const sz1, sz2: WideString): Integer; stdcall;
implementation
function StrCmpLogicalW; external 'shlwapi.dll' name 'StrCmpLogicalW';
Поскольку переменная WideString
является указателем на WideChar
(аналогично, переменная AnsiString
является указателем на AnsiChar
.)
И таким образом Delphi автоматически "преобразует" AnsiString в WideString
для вас.
Update
И так как теперь мы находимся в мире 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
для вас в сгенерированном коде.
CompareString поддерживает числовую сортировку в Windows 7
Начиная с Windows 7 Microsoft добавила SORT_DIGITSASNUMBERS
в CompareString
:
Windows 7: Обработайте цифры как цифры во время сортировки, например, выполните сортировку "2" до "10".
Ничто из этого не помогает ответить на фактический вопрос, связанный с тем, когда вам нужно преобразовать или передать строки.
Ответ 3
Для вашей функции может быть ANSI-вариант (я не проверял). Большинство Wide API доступны как версия ANSI, просто измените W-суффикс на A, и вы настроены. В этом случае Windows делает прозрачное преобразование прозрачным для вас.
PS: Здесь статья, описывающая отсутствие StrCmpLogicalA: http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogicala.aspx
Ответ 4
Используйте 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 solution выглядит и намного приятнее!