2009-08-26 7 views
18

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

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

Пример кода:

m_longitude = atof((char *)pField); 

Где

pField[] = "01000.3897"; 

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

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

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

+0

Можете ли вы дать нам несколько примеров данных, с которыми вы должны работать? Это может помочь нам обеспечить лучшее решение. – suszterpatt

+0

m_longitude = atof ((char *) pField); где pField [] = "01000.3897"; Кросс-платформенный проект, скомпилированный для Windows XP и CE. – tomash

+0

Есть ли веская причина не использовать strtod (который имеет одинаковую характеристику в локали, но имеет лучшую обработку ошибок)? – AProgrammer

ответ

15

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

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

+0

Он использует глобальный язык C++. Изменение глобального языка C++ изменяет локаль C, если у нее есть имя - если это не влияет на локаль C, это реализация определена. – AProgrammer

+0

@AProgrammer: Вы действительно читали и понимали мой ответ перед комментированием/downvoting? – rjnilsson

+0

@AProgrammer: Хорошо, перечитывая мой ответ, возможно, это было не очень ясно. Тем не менее я никогда не предлагал изменить глобальный язык, просто упомянул, что если кто-то еще это сделал, это повлияет на образец кода. – rjnilsson

6

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

+4

localeconv (в ) возвращает указатель на struct, элемент decimal_point которого содержит это значение. Обратите внимание, что указатель действителен до следующего localeconv() или setlocale() – AProgrammer

2

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

+0

Определенно. Я не могу рисковать каким-либо воздействием на другие части системы, и, возможно, изменение локали может повлиять на другие процессы. – tomash

+1

вызов setlocale влияет только на локаль текущего процесса. Если у вас есть другие потоки, которые выполняют зависящие от локали вещи, они должны быть синхронизированы. – danio

+0

AFAIK под окнами CE локали глобальны, не реплицируются в процессе – tomash

0

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

+0

Misundrestanding. Всегда будет один период, но иногда atof будет ожидать запятую и игнорировать часть доли за период. – tomash

+0

Справа. В этом случае я бы пошел с решением MSalters: напечатал float, получил разделитель, а затем заменил '.' на него. – suszterpatt

0

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

setlocale(LC_ALL|~LC_NUMERIC, ""); 

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

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

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

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

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

_free_locale(plocale); 

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

+0

Нет такого варианта для библиотеки времени выполнения Windows CE (упомянуто в вопросе) – tomash

+0

К сожалению, спасибо за исправление! – Raptormeat

 Смежные вопросы

  • Нет связанных вопросов^_^