Подтвердить что ты не робот

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

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

Каков наилучший метод решения этой проблемы? Строка long/latitude хранится в массиве символов, если это имеет значение.

Пример кода:

m_longitude = atof((char *)pField); 

Где

pField[] = "01000.3897"; 

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

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

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

4b9b3361

Ответ 1

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

#include <sstream>
...

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

istr >> longitude;

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

setlocale(LC_ALL|~LC_NUMERIC, "");

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

Ответ 6

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

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

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

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

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

_free_locale( plocale );

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