【问题标题】:AnsiString to Double C++AnsiString 到 Double C++
【发布时间】:2018-06-10 14:44:43
【问题描述】:

我正在尝试将AnsiString 转换为double,但运行程序后,它显示“20.60 不是有效的浮点数”。

AnsiString Temperatur

RE_Temperatur->Text = Temperatur;

Chart1->Series[0]->AddXY(DT_Uhrzeit->Time, RE_Temperatur->Text.ToDouble(), 
"", clRed);

Temperatur 的值来自我的 Arduino 的 SerialPort 连接,并提供诸如 20.60 之类的东西。当我将字符串剪切到前 2 位时,它工作正常。猜测. 与错误有关,但我不知道如何解决。

编辑 // 我现在试图只替换“。”带有“,”的代码如下:

    RE_Temperatur->Text = StringReplace(RE_Temperatur->Text, ".", ",", 
    TReplaceFlags() << rfReplaceAll);

现在我收到一条错误消息“20,60 不是有效的浮点数”。太令人沮丧了:/

【问题讨论】:

  • 这个AnsiString 类是在哪里定义的?是否有可能它是区域设置感知的,并且您的区域设置有 , 而不是 . 作为小数点分隔符?
  • 我正在使用 Codegear,忘了说,我必须在哪里查找这个?是我正在使用的编译器的设置中的东西吗?
  • 也有可能 Arduino 提供的数据(例如不可打印的字符)无法作为浮点值进行扫描。
  • Arduino 正在将字符串发送到串行端口,在我的 C++ 程序中,我正在将字符串转换为 Ansistring 以将其放入我的编辑框中。
  • @Angew - AnsiString 是与 Borland/Inprise/Embarcadero C++ Builder 产品相关的供应商特定类型。

标签: c++builder ansistring


【解决方案1】:

在新的控制台程序中尝试这段简单的代码:

AnsiString Temperatur = "12.45"; // locale independent
AnsiString Temperatur2 = "12,45"; // correct in my German locale

double temp = StrToFloat(Temperatur, TFormatSettings::Invariant());
double temp2 = StrToFloat(Temperatur2); // In my case: German locale!

printf("%8.4f %8.4f\n", temp, temp2);

输出是:

 12.4500  12.4500

您可以看到它按预期工作。请注意,在我的系统上,comma 是区域设置的小数分隔符,但由于格式设置不变,此代码可以很好地使用句点作为小数分隔符。

所以做出选择:如果您需要独立于语言环境,请使用 TFormatSettings::Invariant(),但如果您希望它使用用户语言环境的小数分隔符,请不要使用它。

【讨论】:

    【解决方案2】:

    我敢打赌,你的问题是小数点。用于它的字符是 Windows 环境变量之一,并且可能因计算机而异。有 winapi 调用来接收它,但我不记得它们......

    AnsiString("1.23").ToDouble() 的问题是catch/try 不会捕获异常(至少在我的编译器BDS2006 上)。有解决方法。我使用的是atoi(),它不会崩溃,而是在存在无效字符时截断为整数,因此它可用于检测正确的小数点字符和转换:

    char _floating_point_char=' ';         // decimal point separator
    AnsiString   strnum (AnsiString s,int sig=1,int pnt=1,int hex=0,char dot=_floating_point_char);
    double       str2num(AnsiString s,int sig=1,int pnt=1,int hex=0,char dot=_floating_point_char);
    
    AnsiString strnum(AnsiString s,int sig,int pnt,int hex,char dot)
        {
        if (dot==' ')
            {
            float x;
            x=atof("0.5"); if (x>=0.25) _floating_point_char='.';
            x=atof("0,5"); if (x>=0.25) _floating_point_char=',';
            dot=_floating_point_char;
            }
    
        int i,l,a,e,e0,exp=pnt;
        AnsiString q="";
        l=s.Length();
        if (hex) exp=0;     // exponent is e,E so it colide with hex e,E
        e0=0; for (i=1;i<=l;i++)
            {
            e=0;
            a=s[i];
            if ((a>='0')&&(a<='9'))       { e=1; q+=char(a  ); sig=0; }
            if ((a>='A')&&(a<='F'&&(hex))){ e=1; q+=char(a  ); sig=0; }
            if ((a>='a')&&(a<='f'&&(hex))){ e=1; q+=char(a  ); sig=0; }
            if ((a=='e')||(a=='E'))
             if (!hex)   { if (!exp) break; e=1; q+=char('e'); exp=0; sig=1; pnt=0; e0=0; }
            if (a=='+')  { if (!sig) break; e=1;               sig=0; }
            if (a=='-')  { if (!sig) break; e=1; q+=char(a  ); sig=0; }
            if (a=='.')  { if (!pnt) break; e=1; q+=char(dot); pnt=0; }
            if (a==',')  { if (!pnt) break; e=1; q+=char(dot); pnt=0; }
            if ((e0)&&(!e)) break;
            e0|=e;
            }
        if (q.Length()<=0) q="0";
        return q;
        }
    double str2num(AnsiString s,int sig,int pnt,int hex,char dot)
        {
        return atof(strnum(s,sig,pnt,hex,dot).c_str());
        }
    

    它只是检测.,是否是正确的,然后在转换之前将其替换为字符串...从winapi粗略获取正确的小数点字符更安全,因为小数点分隔符可以是任何东西...您可以忽略hex 部分,我将其用于任何类型的数字...

    【讨论】:

    • trToFloat 将执行的 ISTM,尤其是与 TFormatSettings 一起使用时。
    • @RudyVelthuis 是的,但我使用它,因为我的应用程序在世界各地运行,并且每台机器/语言/locale_settings 上的小数点不同,但 *.ini 和配置文件通常是共享和使用的TFormatSettings 需要知道哪个是我没有的奢侈品,因为每个用户都喜欢不同的东西。这种方法适用于格式错误的字符串,尽管您不知道使用了哪个小数点以及应该使用哪个小数点...
    • 所以使用TFormatSettings::Invariant,这是一个预定义的locale-invariant格式设置。换句话说,全世界都一样。如果您也使用该 TFormatSettings 来写入您的数据,您可以确定它是往返的(即数据输入 == 数据输出)并且不会发生此类问题。否则,如果数据依赖于语言环境,则使用 StrToFloat without 它。这将使用用户的语言环境。
    • FWIW, TFormatSettings::Invariant 主要兼容美国语言环境,所以小数分隔符是点(句号,句点),千位分隔符是逗号(但后者只用于输出,未使用用于输入)。并且当前默认的FormatSettings是用winapi提供的初始化的,所以不需要手动做。
    【解决方案3】:

    您可以使用StrToFloat,它有第二个版本,它采用TFormatSettings 对象,您可以使用它来指定特定的DecimalSeperator。顺便说一句,它不会返回浮点数,因为它的名称可能暗示它返回Extended aka long double

    【讨论】:

    • 感谢您的回答。但我不知道如何在我的代码中实现这一点。你有什么简短的例子吗?
    • 除了特定的 TFormatSetitngs,您还可以使用预定义的 Invariant FormatSettings(参见帮助)来获得与语言无关的版本,'.' 作为 DecimalSeparator。
    • @kevin: Chart1-&gt;Series[0]-&gt;AddXY(DT_Uhrzeit-&gt;Time, StrToFloat(Temperatur, TFormatSettings::Invariant()), "", clRed);
    • FWIW,之所以称为StrToFloat,是因为它将字符串转换为浮点值。在 Delphi 中,float 类型称为Single,用于单精度。在设计它的时候,与 C 或 C++ 的兼容性还不是什么大问题,所以他们从未考虑将其称为 StrToExtended 或类似的名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2020-08-11
    • 2012-07-16
    • 1970-01-01
    相关资源
    最近更新 更多