Локально-независимый "atof"? - PullRequest
       21

Локально-независимый "atof"?

21 голосов
/ 26 августа 2009

Я анализирую записи статуса GPS в фиксированных предложениях NMEA, где дробная часть географических минут всегда идет после периода. Однако в системах, где языковой стандарт определяет запятую как десятичный разделитель, функция atof игнорирует точку и целую часть дроби.

Как лучше всего решить эту проблему? Длинная / широковая строка хранится в массиве символов, если это имеет значение.

Пример кода:

m_longitude = atof((char *)pField); 

Где

pField[] = "01000.3897"; 

Кроссплатформенный проект, скомпилированный для Windows XP и CE.

Комментарий к решению:

Принятый ответ более элегантен, но этот ответ (и комментарий) также стоит знать как быстрое исправление

Ответы [ 7 ]

16 голосов
/ 26 августа 2009

Вы всегда можете использовать (по модулю проверки ошибок):

#include <sstream>
...

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

istr >> longitude;

Стандартные iostreams по умолчанию используют глобальную локаль (которая, в свою очередь, должна быть инициализирована для классической (США) локали). Таким образом, вышеприведенное должно работать в целом, если кто-то ранее не изменил глобальную локаль на что-то другое, даже если вы работаете на неанглоязычной платформе. Чтобы быть абсолютно уверенным, что используется требуемый языковой стандарт, создайте конкретный языковой стандарт и «наполните» поток этим языковым стандартом, прежде чем читать из него:

#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 - минутам (но я полагаю, вы уже это знали).

7 голосов
/ 26 августа 2009

Одно неприятное решение, которое я однажды сделал, - sprintf() 0.0f и извлечение второго символа из вывода. Затем во входной строке замените '.' этим персонажем. Это решает запятую, но также работает, если локаль определила другие десятичные разделители.

2 голосов
/ 26 августа 2009

Любая причина, почему вы не можете сделать setlocale"C" перед atof и восстановить локаль после этого? Может быть, я неправильно понял вопрос ...

0 голосов
/ 13 июля 2017

Я полагаю, что самым простым ответом на этот конкретный вопрос было бы использование версии atof(), которая принимает параметр языка C:

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

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

_free_locale( plocale );

Это позволяет вам вообще не связываться с потоками, глобальной локалью или манипулированием строкой. Просто создайте нужный объект локали, чтобы выполнить всю его обработку, а затем освободите его, когда вы закончите.

0 голосов
/ 25 июня 2017

Некоторые из приведенных выше решений, похоже, не работают, поэтому я предлагаю это как полностью отказоустойчивое решение. Просто скопируйте и вставьте эту функцию и используйте ее.

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;
}
0 голосов
/ 26 августа 2009

Вам действительно нужно получить поведение локали для чисел? Если нет

setlocale(LC_ALL|~LC_NUMERIC, "");

или эквивалентное использование конструктора std :: locale.

0 голосов
/ 26 августа 2009

Вы можете перебрать все символы в массиве и поменять местами любые не числа с символом ., который должен работать, пока координаты находятся в формате number-single_delimiter_character_-number.

...