Нужно ли конвертировать строку в WideString в Delphi? - PullRequest
18 голосов
/ 21 июня 2009

Я нашел функцию 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;

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

Ответы [ 4 ]

11 голосов
/ 22 июня 2009

Имейте в виду, что приведение строки к WideString преобразует ее с использованием системной кодовой страницы по умолчанию, которая может быть, а может и не быть тем, что вам нужно. Как правило, вы хотите использовать локаль текущего пользователя.

С WCharFromChar в System.pas:

Result := MultiByteToWideChar(DefaultSystemCodePage, 0, CharSource, SrcBytes,
  WCharDest, DestChars);

Вы можете изменить DefaultSystemCodePage, вызвав SetMultiByteConversionCodePage .

5 голосов
/ 03 мая 2012

Самый простой способ выполнить задачу - объявить вашу функцию как:

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 для вас в сгенерированном коде.

CompareString поддерживает числовую сортировку в Windows 7

Начиная с Windows 7, Microsoft добавила SORT_DIGITSASNUMBERS к CompareString:

Windows 7: При сортировке обрабатывать цифры как числа, например, сортировать «2» перед «10».

Ничто из этого не помогает ответить на актуальный вопрос, который касается случаев, когда вам нужно преобразовать или привести строки.

3 голосов
/ 21 июня 2009

Для вашей функции может быть вариант ANSI (я не проверял). Большинство Wide API доступны также в версии ANSI, просто измените суффикс W на A, и все готово. В этом случае Windows выполняет обратное преобразование для вас.

PS: вот статья, описывающая отсутствие StrCmpLogicalA: http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogicala.aspx

2 голосов
/ 20 апреля 2012

Используйте System.StringToOleStr, который является удобной оболочкой для MultiByteToWideChar, см. Ответ Габра :

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;

Но тогда решение Яна Бойда выглядит и намного приятнее!

...