与地点无关的“atof”?

时间:2009-08-26 09:28:05

标签: c++ locale atof

我正在解析固定NMEA句子中的GPS状态条目,其中地理分钟的一小部分总是在句点之后。但是,在locale将逗号定义为小数分隔符的系统上, atof 函数会忽略句点和整数小数部分。

处理此问题的最佳方法是什么?存储在字符数组中的长/纬度字符串,如果重要的话。

示例代码:

m_longitude = atof((char *)pField); 

哪里

pField[] = "01000.3897"; 

跨平台项目,为Windows XP和CE编译。

对解决方案的评论:

接受的答案更优雅,但this回答(和评论)也值得快速解决

8 个答案:

答案 0 :(得分:16)

您可以随时使用(模数错误检查):

#include <sstream>
...

float longitude = 0.0f;
std::istringstream istr(pField);

istr >> longitude;

默认情况下,标准iostream使用全局语言环境(而后者应该初始化为经典(美国)语言环境)。因此,除非有人以前将全局语言环境更改为其他内容,否则上述内容应该可以正常工作,即使您在非英语平台上运行也是如此。为了确保使用所需的语言环境,请创建一个特定的语言环境,并在读取该语言环境之前“填充”该流:

#include <sstream>
#include <locale>

...
float longitude = 0.0f;
std::istringstream istr(pField);

istr.imbue(std::locale("C"));
istr >> longitude;

作为旁注,我通常使用正则表达式来验证NMEA字段,将字段的不同部分提取为捕获,然后使用上述方法转换不同的部分。 NMEA经度字段中小数点前面的部分实际上格式化为“DDDMM.mmm ..”,其中DDD对应度数,MM.mmm到分钟(但我猜你已经知道了)。

答案 1 :(得分:7)

我曾经做过的一个令人讨厌的解决方案是sprintf() 0.0f并从输出中获取第二个字符。然后在输入字符串中替换'。'通过那个角色。这解决了逗号大小写,但如果语言环境定义了其他小数分隔符,也可以使用。

答案 2 :(得分:4)

这个问题很旧,但是与此同时,在C ++中,我们得到了一个“与语言环境无关”的atof:

在c ++ 17中添加的

std::from_chars(及其同级std::to_chars)提供了与语言环境无关的浮点扫描(和格式设置)。它们位于标题<charconv>中。

您可以在此处了解有关它们的更多信息:

https://en.cppreference.com/w/cpp/utility/from_chars

https://en.cppreference.com/w/cpp/utility/to_chars

我建议Stephan T. Lavavej很好地谈论这两个工具,这是他谈论使用std :: from_chars的部分的链接: https://youtu.be/4P_kbF0EbZM?t=1367

还有我的一个简短示例:

#include <charconv>
#include <iostream>
#include <system_error>

int main()
{
    char buffer[16] { "123.45678" };
    float result;
    auto [p, ec] = std::from_chars(std::begin(buffer), std::end(buffer), result);
    if(ec == std::errc{})
        std::cout << result;
}

不幸的是,就今天(2020年6月6日)而言,仅MSVC支持使用浮点类型的这些功能。高效地实施它们是一个大问题。

答案 3 :(得分:2)

为什么你不能在atof之前做setlocale“C”并之后恢复语言环境?也许我误解了这个问题......

答案 4 :(得分:0)

您可以遍历数组中的所有字符,并使用.字符交换任何非数字,只要坐标为number-single_delimiter_character_-number格式,该字符就可以正常工作。

答案 5 :(得分:0)

你真的需要获得数字的语言环境行为吗?如果不是

setlocale(LC_ALL|~LC_NUMERIC, "");

或等效使用std :: locale构造函数。

答案 6 :(得分:0)

上面的一些解决方案似乎没有用,所以我认为这是一个完全防故障的解决方案。只需复制粘贴此功能并改为使用它。

float stor(const char* str) {
    float result = 0;
    float sign = *str == '-' ? str++, -1 : 1;
    while (*str >= '0' && *str <= '9') {
        result *= 10;
        result += *str - '0';
        str++;
    }
    if (*str == ',' || *str == '.') {
        str++;
        float multiplier = 0.1;
        while (*str >= '0' && *str <= '9') {
            result += (*str - '0') * multiplier;
            multiplier /= 10;
            str++;
        }
    }
    result *= sign;
    if (*str == 'e' || *str == 'E') {
        str++;
        float powerer = *str == '-'? str++, 0.1 : 10;
        float power = 0;
        while (*str >= '0' && *str <= '9') {
            power *= 10;
            power += *str - '0';
            str++;
        }
        result *= pow(powerer, power);
    }
    return result;
}

答案 7 :(得分:0)

我相信这个具体问题的最简单答案是使用atof()的版本,该版本采用C语言环境参数:

_locale_t plocale = _create_locale( LC_ALL, "C" );

double result = _atof_l( "01000.3897", plocale );

_free_locale( plocale );

这使您可以根本不弄乱流,全局语言环境或操作字符串。只需创建所需的区域设置对象即可完成所有处理,然后在完成后将其释放。