Локально-независимый "atof"?
Я разбираю записи состояния GPS в фиксированных предложениях NMEA, где часть географических минут поступает всегда после периода. Однако в системах, где локаль определяет запятую как десятичный разделитель, функция atof игнорирует часть периода и целую часть.
Каков наилучший метод решения этой проблемы? Строка long/latitude хранится в массиве символов, если это имеет значение.
Пример кода:
m_longitude = atof((char *)pField);
Где
pField[] = "01000.3897";
Кросс-платформенный проект, скомпилированный для Windows XP и CE.
Комментарий к решению:
Принятый ответ более изящный, но этот ответ (и комментарий) также стоит знать как быстрое исправление
Ответы
Ответ 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 );
Это позволяет вам не вмешиваться в потоки, глобальную локаль или вообще не манипулировать строкой. Просто создайте желаемый объект локали, чтобы выполнить всю свою обработку, а затем освободите его, когда закончите.