如何将浮点数或货币转换为本地化字符串?

2023-12-19

In Delphi1, using FloatToStrF or CurrToStrF will automatically use the DecimalSeparator character to represent a decimal mark. Unfortunately DecimalSeparator is declared in SysUtils as Char1,2 http://www.delphibasics.co.uk/RTL.asp?Name=DecimalSeparator:

var 
  DecimalSeparator: Char;

虽然LOCALE_SDECIMAL http://msdn.microsoft.com/en-us/library/dd373841%28v=vs.85%29.aspx最多允许三个字符:

用于小数点分隔符的字符,例如“.”在“3.14”或“3,14”中的“,”。该字符串允许的最大字符数为四个,包括终止空字符。

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

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

在我的电脑上,这是一个很有个性的人 http://www.siao2.com/2010/02/16/9964017.aspx,这会导致浮点和货币值无法正确本地化U+002E http://www.fileformat.info/info/unicode/char/2e/index.htm(句号)小数点。

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

  • GetNumberFormat http://msdn.microsoft.com/en-us/library/dd318110%28v=vs.85%29.aspx
  • GetCurrencyFormat http://msdn.microsoft.com/en-us/library/dd318083%28v=VS.85%29.aspx

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

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

What would be a good way1 to convert a floating point, or currency, value to a string that obeys those rules? e.g.

  • 1234567.893332
  • -1234567

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

  • 可能不会使用-表示否定(例如--)
  • 可能不会使用.表示小数点 http://en.wikipedia.org/wiki/Decimal_mark (e.g. ,,)
  • 可能不使用拉丁字母0123456789来表示数字 http://blogs.msdn.com/b/oldnewthing/archive/2004/03/09/86555.aspx (e.g. [删除了导致 JavaScript 解析器崩溃的阿拉伯数字])

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

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 使用的函数链的附加信息:

  • FloatToStrF http://docwiki.embarcadero.com/VCL/en/SysUtils.FloatToStrF and CurrToStrF http://docwiki.embarcadero.com/VCL/en/SysUtils.CurrToStrF calls:
    • FloatToText http://docwiki.embarcadero.com/VCL/en/SysUtils.FloatToText calls:
      • FloatToDecimal http://docwiki.embarcadero.com/VCL/en/SysUtils.FloatToDecimal

Note

  • DecimalSeparator: Char, 单全局字符已被弃用 http://docwiki.embarcadero.com/VCL/en/SysUtils.TFormatSettings.DecimalSeparator,并替换为另一个单字符小数分隔符

1 in my version of Delphi
2 and in current versions of Delphi


Delphi 确实提供了一个名为FloatToDecimal转换浮点数(例如Extended) and Currency值转换为有用的结构以进行进一步格式化。例如。:

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

给你:

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

Where Exponent给出小数点左边的位数。

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

  • 指数为零

       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, or -INF)

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

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


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

然后可以将该字符串传递给适当的 WindowsGetNumberFormat or GetCurrencyFormat函数来执行实际的正确定位。

我自己写的CurrToDecimalString and FloatToDecimalString它将数字转换为所需的独立于区域设置的格式:

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;

除了边缘情况NAN, INF and -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实现留给读者作为练习(因为我实际上还没有编写浮点型,只是货币 - 我不知道如何处理指数表示法)。

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

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

Note:任何代码都会发布到公共领域。无需归属。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将浮点数或货币转换为本地化字符串? 的相关文章

  • 如何在程序中使用非常大的字符串(5 亿个字符)?

    我有一个 txt 文件 其中包含 5 亿位 Pi 的二进制表示形式 我需要在我的程序中使用它的字符串表示形式 我还需要能够搜索它的子字符串等 换句话说 我需要能够像正常大小的字符串一样对待它 我会努力寻找一个lot子串 所以速度是必要的 我
  • 在C中查找子字符串在字符串中的位置

    这是一个接受的程序 来自用户的一句话 来自用户的话 如何找到输入的单词在句子中的位置 include
  • 我如何淡入/淡出 TImage?

    我有一个简单的TForm命名为Form1 Image1 是一个TImage加载了一个 PNGImage 和一个 Button1TButton测试事物 成功实现了对图像1的Alpha Blend的方法 代码如下 procedure SetPN
  • 如何在 iOS 和 Android 上获取应用程序恢复状态?

    当应用程序恢复时 是否可以从代码角度检查某些内容iOS and Android 例如当应用程序最小化并恢复时 应用程序仍在设备后台运行 你需要使用IFMX应用程序事件服务 http docwiki embarcadero com Libra
  • 在字符串列表中查找子字符串

    我有一个像这样的列表 我希望能够在此列表中搜索来自另一个字符串的子字符串 例子 List
  • 挂钩到文件,实时读取新内容

    我如何连接到另一个程序当前正在使用的文件 该程序不断向该文件写入内容 假设有一个 10 行的文本文件 当我启动我的应用程序时 每次写入程序保存其内容时 它都必须检测并读取其中的任何内容 可以在不不断检查文件大小 日期的情况下完成此操作吗 您
  • 将字符串数组转换为字节数组

    好吧 在有人试图将其标记为重复之前 我是not要求将字符串转换为字节数组 我想要一个字符串数组 包含类似这样的内容 5 168 188 28 29 155 转换为字节数组 我已经搜索过 只能找到字符串到字节数组 这是完全不同的 谢谢 编辑
  • 替换VBA中的变量字符串

    我需要替换字符串中的某些内容 但替换的内容可能会有所不同 有可能 XY test XXxY test XXyyXx TEST yXyy Test 以及几乎任何其他空格和上述情况的组合 我需要替换 test 部分并保留 XXX 所以 当使用简
  • java为oracle VARCHAR2返回空字符串值

    我有以下代码 它似乎工作正常 但它不显示 personCode 字符串的任何值 PERSON CODE 是 Oracle 9i 数据库中的 VARCHAR2 我在我的项目中使用 Java SE 1 7 和 ojdbc7 jar 我是 Jav
  • 如何在TWebBrowser中显示相对路径图像?

    我正在 DesignMode Doc DesignMode On 中使用 TWebBrowser 来编写 HTML 文档 TWebBrowser 中没有加载文档 磁盘上的 HTML 文件 我直接在 TWebBrowser 中从零开始创建文档
  • Rails 3,i18n:将 html 标签插入文本

    我的问题正是中描述的问题这个问题 https stackoverflow com questions 2543936 rails i18n translating text with links inside 唯一的区别 我使用的是 Rai
  • 关于相同的应用程序,但不同的应用程序商店有不同的二进制文件

    我有一个新的应用程序要在不久的将来推出 该应用程序将针对不同地区的应用程序商店提供不同的版本 包括 UI 语言和部分应用程序内容 我的问题是我是否可以创建不同的应用程序 不同的捆绑包 ID 和不同的应用程序的 iTunes 应用程序名称 但
  • 错误“无法将参数 '1' 的 'std::basic_string' 转换为 'const char*' 到 'int system(const char*)'”

    当我尝试编译脚本时 出现此错误 类型 const char 和 const char 6 到二进制 operator 的操作数无效 这里应该是错误 string name john system quickscan exe resoluti
  • 如何在二维数组中找到字符串?

    我有一个看起来像这样的数组 var array a b c d e f 我希望能够在数组中搜索字符串 d 并返回对应的值 c try function find str array for var i in array if array i
  • VB6中如何将十六进制字符串转换为字节数组

    我有以下字节数组 Dim Template 1023 As Byte 然后我调用指纹扫描仪设备函数并返回以下内容 Template 0 70 Template 1 77 Template 2 82 Template 1023 0 然后我将字
  • 在C#中使用字符串调用变量

    我试图使用循环来编辑多个文本框的数据 但我无法弄清楚如何转换内容是我的框名称的字符串来访问每个框的文本元素 private void reset Click object sender EventArgs e string cell for
  • 将字符串列表转换为字典

    我有一个清单 Tests run 1 Failures 0 Errors 0 我想将其转换为字典 Tests run 1 Failures 0 Errors 0 我该怎么做 Use a Tests run 1 Failures 0 Erro
  • 就 ADO 而言,nvarchar(max) 有多大?

    我正在尝试使用针对 ADO 的参数化查询 http msdn microsoft com en us library windows desktop ms677209 28v vs 85 29 aspx INSERT INTO Foo 名称
  • 如何加速 pandas 字符串函数?

    我正在使用 pandas 矢量化 str split 方法来提取从 上的拆分 返回的第一个元素 我还尝试使用 df apply 与 lambda 和 str split 来产生等效的结果 使用 timeit 时 我发现 df apply 的
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi

随机推荐