与区域设置无关的“atof”?

发布于 2024-08-02 17:57:49 字数 546 浏览 1 评论 0原文

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

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

示例代码:

m_longitude = atof((char *)pField); 

其中

pField[] = "01000.3897"; 

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

对解决方案的评论:

接受的答案更优雅,但是这个< /a> 答案(和评论)也值得了解作为快速解决方案

I'm parsing GPS status entries in fixed NMEA sentences, where fraction part of geographical minutes comes always after period. However, on systems where locale defines comma as decimal separator, atof function ignores period and whole fraction part.

What is the best method to deal with this issue? Long/latitude string in stored in character array, if it matters.

Example Code:

m_longitude = atof((char *)pField); 

Where

pField[] = "01000.3897"; 

Cross-platform project, compiled for Windows XP and CE.

Comment to solution:

Accepted answer is more elegant, but this answer (and comment) is also worth knowing as a quick fix

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

往事随风而去 2024-08-09 17:57:49

您始终可以使用(模错误检查):

#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 对应于分钟(但我想您已经知道了)。

You could always use (modulo error-checking):

#include <sstream>
...

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

istr >> longitude;

The standard iostreams use the global locale by default (which in turn should be initialized to the classic (US) locale). Thus the above should work in general unless someone previously has changed the global locale to something else, even if you're running on a non-english platform. To be absolutely sure that the desired locale is used, create a specific locale and "imbue" the stream with that locale before reading from it:

#include <sstream>
#include <locale>

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

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

As a side note, I've usually used regular expressions to validate NMEA fields, extract the different parts of the field as captures, and then convert the different parts using the above method. The portion before the decimal point in an NMEA longitude field actually is formatted as "DDDMM.mmm.." where DDD correspond to degrees, MM.mmm to minutes (but I guess you already knew that).

伴随着你 2024-08-09 17:57:49

我曾经做过的一个令人讨厌的解决方案是使用 sprintf() 0.0f 并从输出中获取第二个字符。然后在输入字符串中替换“.”通过那个角色。这解决了逗号的情况,但如果区域设置定义了其他小数点分隔符,也可以工作。

A nasty solution I've done once is to sprintf() 0.0f and grab the second character from the output. Then in the input string replace '.' by that character. This solves the comma case, but would also work if a locale defined other decimal separators.

知足的幸福 2024-08-09 17:57:49

这个问题很老了,但与此同时,在 C++ 中,我们得到了一个“独立于语言环境”的 atof:

std::from_chars (及其兄弟 std::to_chars),添加在 c++17 中,提供与区域设置无关的浮点扫描(和格式化)。它们位于标头 中。

您可以在这里阅读有关它们的更多信息:

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 月 5 日),只有 MSVC 支持这些浮点类型的函数。事实证明,有效地实施它们是一个大问题。

@edit (2021 年 4 月 27 日) 今天发布的 libstdc++ 与稳定的 GCC 11.1 增加了对浮点类型 的支持。然而,这个实现似乎不符合标准 - 它需要将文本复制到另一个缓冲区中,并使用默认的 C 语言环境调用 strto(f/d/ld) 并设置浮动环境,从而产生错误错误号。在极其奇怪的情况下,它可以在下面分配、抛出和捕获异常。您可以在这里找到实现:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B17/floating_from_chars.cc#L304

This question is old, but in the meantime in C++ we got a "locale-independent" atof:

std::from_chars (with its sibling std::to_chars), added in c++17, provide locale-independent float scanning (and formatting). They are located in header <charconv>.

You can read more about them here:

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

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

I recomment Stephan T. Lavavej wonderful talk about these two tools, here's the link to the part where he talks about using std::from_chars:
https://youtu.be/4P_kbF0EbZM?t=1367

And a short example by me:

#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;
}

Unfortunately, as for today (05.06.2020) only MSVC supports these functions with floating types. Implementing them efficiently turned out to be a big problem.

@edit (27.04.2021) libstdc++ released today with stable GCC 11.1 adds support for floating-type <charconv>. However, this implementation seems to be not standard-compliant - it needs to copy the text into another buffer and calls strto(f/d/ld) with default C locale and set Floating Environment, taking error from errno. In extremely weird cases it can allocate, throw and catch exceptions underneath. You can find the implementation here:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B17/floating_from_chars.cc#L304

桃扇骨 2024-08-09 17:57:49

出于任何原因,您不能在atof 并随后恢复语言环境?也许我误解了这个问题......

Any reason why you can't do a setlocale "C" before the atof and restore the locale afterwards? Maybe I misunderstood the question...

柒七 2024-08-09 17:57:49

您可以迭代数组中的所有字符,并将任何非数字与 . 字符交换,只要坐标位于 number-single_delimiter_character_-number 中,该字符就应该有效> 格式。

You could iterate through all the characters in the array and swap any non-numbers with a . character, which should work as long as the coordinates are in a number-single_delimiter_character_-number format.

ぺ禁宫浮华殁 2024-08-09 17:57:49

您真的需要获取数字的区域设置行为吗?如果没有

setlocale(LC_ALL|~LC_NUMERIC, "");

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

Do you really need to get locale behavior for numerics? If not

setlocale(LC_ALL|~LC_NUMERIC, "");

or the equivalent use of std::locale constructor.

两人的回忆 2024-08-09 17:57:49

上面的一些解决方案似乎不起作用,所以我建议这是一个完美的防故障解决方案。只需复制粘贴该函数并使用它即可。

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;
}

Some of the solutions above did not seem to work, so I propose this as a perfectly failproof solution. Just copy-paste this function and use it instead.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文