【问题标题】:What is the most simple way to check if a string may convert to AnsiString safely in XE4 and above?检查字符串是否可以在 XE4 及更高版本中安全地转换为 AnsiString 的最简单方法是什么?
【发布时间】:2014-06-22 14:12:09
【问题描述】:

在 Delphi XE4 及以上版本中,我们可能会这样写:

function TestAnsiCompatible(const aStr: string): Boolean;
begin
end;

string 在 Delphi XE4 中被声明为 UnicodeString。它可能包含一个 unicode 字符串。

如果我们进行一些类型转换:

function TestAnsiCompatible(const aStr: string): Boolean;
var a: AnsiString;
begin
  a := aStr;
  Result := a = aStr;
end;

一些编译器警告应该提示:

[dcc32 Warning]: W1058 Implicit string cast with potential data loss from 'string' to 'AnsiString'
[dcc32 Warning]: W1057 Implicit string cast from 'AnsiString' to 'string'

有没有一种简单明了的方法来测试aStr 是否与 AnsiString 完全兼容?或者我们将逐个字符检查:

function TestAnsiCompatible(const aStr: string): Boolean;
var C: Char;
begin
  Result := True;
  for C in aStr do begin
    if C > #127 then begin
      Result := False;
      Break;
    end;
  end;
end;

【问题讨论】:

  • 我希望在带有IS_TEXT_UNICODE_ASCII16 标志的IsTextUnicode 函数中。运气不好。
  • “逐个字符”功能不是解决问题,而是做不同的事情。
  • 你能解释一下为什么你认为#256 很特别吗?
  • 我认为不在 #0..#255 范围内的字符是 AnsiChar?如果转换为 AnsiString,XE 会将这些字符转换为 ?
  • @ChauCheeYang - 请记住 ASCII 范围之外的 AnsiChar 值的含义(即 >= #128)取决于代码页。这意味着相同的 UnicodeString 可以无损失地转换为带有(例如)“Latin-1”代码页集的 AnsiString,但可以转换为带有希腊代码页集的 AnsiString。

标签: delphi unicode


【解决方案1】:

您所要做的就是将警告类型化掉:

function TestAnsiCompatible(const aStr: string): Boolean;
var
  a: AnsiString;
begin
  a := AnsiString(aStr);
  Result := String(a) = aStr;
end;

可以简化为:

function TestAnsiCompatible(const aStr: string): Boolean;
begin
  Result := String(AnsiString(aStr)) = aStr;
end;

【讨论】:

  • 使用字符串比较是可行的,但它似乎是一个昂贵的计算。但我认为这是迄今为止唯一的解决方案。
  • 如果你想确保 Ansi 数据在没有任何损失的情况下往返回 Unicode,那么可以。需要进行实际转换。否则,如果您只想知道转换为 Ansi 是否有损,请尝试调用LocaleCharsFromUnicode()WideCharToMultiByte(),指定WC_NO_BEST_FIT_CHARS 标志,然后检查UsedDefaultChar 参数的输出。
  • 编译器是否从不将 String(AnsiString() 优化为
  • @David:不,我不这么认为。我认为这种显式转换不会被优化掉,正是为了使上述代码成为可能。
  • @DavidSchwartz:不,它没有被优化掉。
【解决方案2】:

我曾经检查 String(a) = AnsiString(a),直到我有一个用户将数据从一台 PC 传输到另一台 PC,并且具有不同的代码页。然后无法正确读回数据。然后我将“安全”的定义更改为“字符串是代码页 1252”(因为这是我的大多数用户所在的区域)。然后在读回我的数据时,我知道我必须将字符串从代码页 1252 转换回来。

function StringIs1252(const S: UnicodeString): Boolean;
// returns True if a string is in codepage 1252 (Western European (Windows))
// Cyrillic is 1251
const
  WC_NO_BEST_FIT_CHARS = $00000400;
var
  UsedDefaultChar: BOOL;   // not Boolean!!
  Len: Integer;
begin
  if Length(S) = 0 then
    Exit(True);
  UsedDefaultChar := False;
  Len := WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, PWideChar(S), Length(S), nil, 0, nil, @UsedDefaultChar);
  if Len <> 0 then
    Result := not UsedDefaultchar
  else
    Result := False;
end;

但是,如果您想检查您的字符串是否可以安全地转换为 ansi - 完全独立于写入或读取时使用的代码页,那么您应该检查所有字符是否在 #0..# 的范围内127.

【讨论】:

  • #0..#127 是 7 位 ASCII,由代码页 20127 表示,因此您可以使用上面相同的代码来处理它。
猜你喜欢
  • 1970-01-01
  • 2012-07-13
  • 2012-01-07
  • 2014-12-14
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多