Подтвердить что ты не робот

Нужно ли преобразовать строку в 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;

Я очень мало знаю о кодировке символов, так что это причина моего вопроса. Является ли эта функция ОК или мне нужно сначала преобразовать обе сравниваемые строки?

4b9b3361

Ответ 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 выглядит и намного приятнее!