【问题标题】:Is it necessary to convert string to WideString in Delphi?Delphi中是否需要将字符串转换为WideString?
【发布时间】:2010-11-04 16:50:34
【问题描述】:

我发现了一个执行字符串“自然比较”的 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;

我对字符编码知之甚少,所以这就是我提出问题的原因。这个函数可以吗,还是我应该先以某种方式转换两个比较的字符串?

【问题讨论】:

    标签: delphi string widestring


    【解决方案1】:

    您的函数可能有一个 ANSI 变体(我尚未检查)。大多数 Wide API 也可以作为 ANSI 版本使用,只需将 W 后缀更改为 A,就可以了。在这种情况下,Windows 会透明地为您执行来回转换。

    PS:这里有一篇描述缺少 StrCmpLogicalA 的文章:http://blogs.msdn.com/joshpoley/archive/2008/04/28/strcmplogicala.aspx

    【讨论】:

    • 任何 Windows API 的任何A 版本都会简单地将“AnsiString”转换为“WideString”,然后调用“Wide”API。作者应该刚刚执行了相同的MultiByteToWideChar。但在 Delphi 中,将 string 转换为 WideString 并让编译器为您调用它非常简单。
    【解决方案2】:

    请记住,将字符串转换为 WideString 将使用默认系统代码页将其转换,这可能是您需要的,也可能不是。通常,您希望使用当前用户的语言环境。

    来自 System.pas 中的WCharFromChar

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

    您可以通过调用 SetMultiByteConversionCodePage 来更改 DefaultSystemCodePage。

    【讨论】:

    • 说实话,我想创建一个与 AnsiCompareText() 对应的函数(当然,这个函数执行 ANSI 字符串的词法比较)。它是否使用当前用户的语言环境或默认系统代码页?谢谢。
    • 使用源:结果 := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1), Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL;并来自 MSDN: LOCALE_USER_DEFAULT 当前用户的默认语言环境。所以不,它不使用当前语言环境,而是使用当前用户的默认语言环境。这又可能是也可能不是您真正想要的:-/
    【解决方案3】:

    使用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 看起来更漂亮!

    【讨论】:

    • 请注意,您刚刚在那里泄露了两个宽字符串。 StringToOleStr 返回一个PWideChar,您需要通过调用CoTaskMemFree(或其legacy moral equivalents 之一)来释放它
    • 哦,有人(有足够的耐心来创建 BDN 登录名)可能应该编辑 StringToOleStr 的 doc wiki 条目,说类似 "备注: 您可以使用 SysFreeString" 释放使用 StringToOleStr 创建的字符串。这样,文档记录了使用 API 的正确方法。
    • 抱歉,我在这里遗漏了什么吗? W1和W2变量不是应该传递给StrCmpLogicalW函数,而不是在函数调用中重复字符串转换吗?
    【解决方案4】:

    完成任务的更简单方法是将函数声明为:

    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 开始,微软将SORT_DIGITSASNUMBERS 添加到CompareString

    Windows 7:在排序时将数字视为数字,例如将“2”排在“10”之前。

    这些都不能帮助回答 实际 问题,该问题涉及何时必须转换或强制转换字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-04
      • 2011-02-11
      • 2014-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-11
      相关资源
      最近更新 更多