gpt4 book ai didi

string - 如何将 float 或货币转换为本地化字符串?

转载 作者:行者123 更新时间:2023-12-03 14:36:32 25 4
gpt4 key购买 nike

在Delphi1中,使用FloatToStrFCurrToStrF将自动使用DecimalSeparator字符来表示小数点。不幸的是DecimalSeparator is declared in SysUtils as Char1,2:

var 
DecimalSeparator: Char;

虽然 LOCALE_SDECIMAL 最多允许三个字符:

Character(s) used for the decimal separator, for example, "." in "3.14" or "," in "3,14". The maximum number of characters allowed for this string is four, including a terminating null character.

这会导致Delphi无法正确读取小数点分隔符;回退到默认的小数点分隔符“.”:

DecimalSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDECIMAL, '.');

在我的计算机 which is quite a character 上,这会导致浮点和货币值错误地使用 U+002E(句号)小数点进行本地化。

愿意直接调用 Windows API 函数,这些函数旨在将浮点或货币值转换为本地化字符串:

除了这些函数采用一串图片代码,其中唯一允许的字符是:

  • 字符“0”到“9”(U+0030..U+0039)
  • 如果数字是浮点值 (U+002E),则为一个小数点 (.)
  • 如果数字为负值,则在第一个字符位置添加减号 (U+002D)

将浮点或货币值转换为遵守这些规则的字符串的好方法是什么1?例如

  • 1234567.893332
  • -1234567

假设本地用户的区域设置(即我的计算机):

<小时/>

一个可怕的、可怕的黑客,我可以使用:

function FloatToLocaleIndependantString(const v: Extended): string;
var
oldDecimalSeparator: Char;
begin
oldDecimalSeparator := SysUtils.DecimalSeparator;
SysUtils.DecimalSeparator := '.'; //Windows formatting functions assume single decimal point
try
Result := FloatToStrF(Value, ffFixed,
18, //Precision: "should be 18 or less for values of type Extended"
9 //Scale 0..18. Sure...9 digits before decimal mark, 9 digits after. Why not
);
finally
SysUtils.DecimalSeparator := oldDecimalSeparator;
end;
end;
<小时/>

有关 VCL 使用的函数链的其他信息:

注意

1 在我的 Delphi 版本中
2 以及当前版本的 Delphi

最佳答案

Delphi确实提供了一个名为FloatToDecimal的过程,它将浮点(例如扩展)和货币值转换为有用的结构以进一步格式化。例如:

FloatToDecimal(..., 1234567890.1234, ...);

给你:

TFloatRec
Digits: array[0..20] of Char = "12345678901234"
Exponent: SmallInt = 10
IsNegative: Boolean = True

其中指数给出小数点左边的位数。

有一些特殊情况需要处理:

  • 指数为零

       Digits: array[0..20] of Char = "12345678901234"
    Exponent: SmallInt = 0
    IsNegative: Boolean = True

    表示小数点左边没有数字,例如.12345678901234

  • 指数为负

       Digits: array[0..20] of Char = "12345678901234"
    Exponent: SmallInt = -3
    IsNegative: Boolean = True

    表示您必须在小数点和第一位数字之间添加零,例如.00012345678901234

  • 指数为-32768(NaN,不是数字)

       Digits: array[0..20] of Char = ""
    Exponent: SmallInt = -32768
    IsNegative: Boolean = False

    表示该值不是数字,例如NAN

  • 指数为 32767(INF-INF)

       Digits: array[0..20] of Char = ""
    Exponent: SmallInt = 32767
    IsNegative: Boolean = False

    表示该值为正无穷大或负无穷大(取决于 IsNegative 值),例如-INF

<小时/>

我们可以使用FloatToDecimal作为起点来创建与区域设置无关的“图片代码”字符串。

然后可以将此字符串传递给适当的 Windows GetNumberFormatGetCurrencyFormat 函数以执行实际的正确本地化。

我编写了自己的 CurrToDecimalStringFloatToDecimalString ,它们将数字转换为所需的区域设置独立格式:

class function TGlobalization.CurrToDecimalString(const Value: Currency): string;
var
digits: string;
s: string;
floatRec: TFloatRec;
begin
FloatToDecimal({var}floatRec, Value, fvCurrency, 0{ignored for currency types}, 9999);

//convert the array of char into an easy to access string
digits := PChar(Addr(floatRec.Digits[0]));

if floatRec.Exponent > 0 then
begin
//Check for positive or negative infinity (exponent = 32767)
if floatRec.Exponent = 32767 then //David Heffernan says that currency can never be infinity. Even though i can't test it, i can at least try to handle it
begin
if floatRec.Negative = False then
Result := 'INF'
else
Result := '-INF';
Exit;
end;

{
digits: 1234567 89
exponent--------^ 7=7 digits on left of decimal mark
}
s := Copy(digits, 1, floatRec.Exponent);

{
for the value 10000:
digits: "1"
exponent: 5
Add enough zero's to digits to pad it out to exponent digits
}
if Length(s) < floatRec.Exponent then
s := s+StringOfChar('0', floatRec.Exponent-Length(s));

if Length(digits) > floatRec.Exponent then
s := s+'.'+Copy(digits, floatRec.Exponent+1, 20);
end
else if floatRec.Exponent < 0 then
begin
//check for NaN (Exponent = -32768)
if floatRec.Exponent = -32768 then //David Heffernan says that currency can never be NotANumber. Even though i can't test it, i can at least try to handle it
begin
Result := 'NAN';
Exit;
end;

{
digits: .000123456789
^---------exponent
}

//Add zero, or more, "0"'s to the left
s := '0.'+StringOfChar('0', -floatRec.Exponent)+digits;
end
else
begin
{
Exponent is zero.

digits: .123456789
^
}
if length(digits) > 0 then
s := '0.'+digits
else
s := '0';
end;

if floatRec.Negative then
s := '-'+s;

Result := s;
end;

除了 NANINF-INF 的边缘情况外,我现在可以将这些字符串传递给 Windows:

class function TGlobalization.GetCurrencyFormat(const DecimalString: WideString; const Locale: LCID): WideString;
var
cch: Integer;
ValueStr: WideString;
begin
Locale
LOCALE_INVARIANT
LOCALE_USER_DEFAULT <--- use this one (windows.pas)
LOCALE_SYSTEM_DEFAULT
LOCALE_CUSTOM_DEFAULT (Vista and later)
LOCALE_CUSTOM_UI_DEFAULT (Vista and later)
LOCALE_CUSTOM_UNSPECIFIED (Vista and later)
}

cch := Windows.GetCurrencyFormatW(Locale, 0, PWideChar(DecimalString), nil, nil, 0);
if cch = 0 then
RaiseLastWin32Error;

SetLength(ValueStr, cch);
cch := Windows.GetCurrencyFormatW(Locale, 0, PWideChar(DecimalString), nil, PWideChar(ValueStr), Length(ValueStr));
if (cch = 0) then
RaiseLastWin32Error;

SetLength(ValueStr, cch-1); //they include the null terminator /facepalm
Result := ValueStr;
end;

The FloatToDecimalString and GetNumberFormat implementations are left as an exercise for the reader (since i actually haven't written the float one yet, just the currency - i don't know how i'm going to handle exponential notation).

还有鲍勃的你叔叔; Delphi 下正确本地化的 float 和货币。

我已经完成了正确本地化整数、日期、时间和日期时间的工作。

Note: Any code is released into the public domain. No attribution required.

关于string - 如何将 float 或货币转换为本地化字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7069204/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com