2009-08-26 16 views
18

Piszę pozycje statusu GPS w stałych zdaniach NMEA, gdzie część ułamków geograficznych minut przychodzi zawsze po kropce. Jednak w systemach, w których locale definiuje przecinek jako separator dziesiętny, funkcja atof ignoruje okres i całą część ułamkową.Niezależne od lokalizacji "atof"?

Jaka jest najlepsza metoda rozwiązania tego problemu? Długi/szerokość geograficzna w przechowywanych w tablicy znaków, jeśli to ma znaczenie.

Przykład Kod:

m_longitude = atof((char *)pField); 

Gdzie

pField[] = "01000.3897"; 

projekt cross-platform, skompilowane dla systemu Windows XP i CE.

Komentarz do rozwiązania:

Przyjęte rozwiązanie jest bardziej elegancki, ale this odpowiedź (i komentarz) Warto też wiedzieć, jak szybko naprawić

+0

Czy możesz podać nam kilka przykładów danych, z którymi musisz pracować? To może nam pomóc w lepszym rozwiązaniu. – suszterpatt

+0

m_longitude = atof ((char *) pField); gdzie pField [] = "01000.3897"; Projekt międzyplatformowy, skompilowany dla systemów Windows XP i CE. – tomash

+0

Czy istnieje uzasadniony powód, aby nie używać strtod (który ma tę samą charakterystykę w ustawieniach regionalnych, ale ma lepszą obsługę błędów)? – AProgrammer

Odpowiedz

15

Można zawsze używać (modulo Sprawdzanie błędów):

#include <sstream> 
... 

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

istr >> longitude; 

Standardowe iostreams użyć globalnego locale domyślnie (co z kolei powinno być inicjowane do klasycznej (US) Ustawienia regionalne). Dlatego powyższe powinno działać ogólnie, chyba że ktoś wcześniej zmienił globalne ustawienia regionalne na coś innego, nawet jeśli używasz platformy innej niż angielska. Aby mieć absolutną pewność, że pożądany locale jest używany, należy utworzyć konkretne lokum i „nasycić” strumień z tej lokalizacji przed przeczytaniem od niego:

#include <sstream> 
#include <locale> 

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

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

Na marginesie, mam zazwyczaj wykorzystywane do sprawdzania poprawności wyrażeń regularnych Pola NMEA, wyodrębnij różne części pola jako przechwycone, a następnie przekonwertuj różne części za pomocą powyższej metody. Część przed kropką dziesiętną w polu długości NMEA jest w rzeczywistości sformatowana jako "DDDMM.mmm ..", gdzie DDD odpowiada stopniom, MM.mmm do minut (ale wydaje mi się, że już to wiedziałeś).

+0

Używa globalnego języka C++. Modyfikowanie globalnego ustawienia narodowego C++ modyfikuje ustawienia regionalne C, jeśli ma nazwę - jeśli nie ma wpływu na definicję locale C, to zdefiniowana jest implementacja. – AProgrammer

+0

@AProgrammer: Czy przeczytałeś i zrozumiałeś moją odpowiedź przed komentarzem/notatką? – rjnilsson

+0

@AProgrammer: OK, ponowne przeczytanie mojej odpowiedzi mogło nie być bardzo jasne. Niemniej jednak nigdy nie zasugerowałem zmiany globalnych ustawień regionalnych, wspomniałem tylko, że gdyby ktoś inny to zrobił, wpłynie to na przykładowy kod. – rjnilsson

6

Paskudny rozwiązanie Robiłem kiedyś ma sprintf() 0.0f i zgarnij drugi znak z wyjścia. Następnie w łańcuchu wejściowym zastąp "." przez tę postać. Rozwiązuje to przypadek przecinka, ale działałoby również, gdyby ustawienia regionalne definiowały inne separatory dziesiętne.

+4

localeconv (w ) zwraca wskaźnik do struktury, której element decimal_point zawiera tę wartość. Zauważ, że wskaźnik jest ważny aż do następnego localeconv() lub setlocale() – AProgrammer

2

Czy jest jakiś powód, dla którego nie możesz wykonać setlocale "C" przed atofem i później przywrócić ustawienia regionalne? Być może źle zrozumiałem pytanie ...

+0

Zdecydowanie. Nie mogę ryzykować żadnego wpływu na inne części systemu, a zmiana ustawień narodowych na pewno może wpłynąć na inne procesy. – tomash

+1

Wywołanie setlocale wpływa tylko na ustawienia narodowe bieżącego procesu. Jeśli masz inne wątki, które wykonują ustawienia zależne od ustawień lokalnych, będą wymagały synchronizacji. – danio

+0

AFAIK pod Windows CE locale są globalne, nie są replikowane na proces – tomash

0

Możliwa jest iteracja wszystkich znaków w tablicy i zamiana dowolnych liczb za pomocą znaku ., który powinien działać tak długo, jak współrzędne są w formacie number-single_delimiter_character_-number.

+0

Nieporozumienie. Zawsze będzie jeden okres, ale czasami będzie oczekiwać przecinka i zignorować część ułamkową po kropce. – tomash

+0

Dobrze. W takim przypadku skorzystam z rozwiązania MSalters: wydrukuj float, uzyskaj separator, a następnie zastąp go znakiem '.'. – suszterpatt

0

Czy naprawdę potrzebujesz zachowania lokalizacji dla numerycznych? Jeśli nie, to konieczne jest użycie innego konstruktora std :: locale.

0

Niektóre z powyższych rozwiązań nie sprawdziły się, dlatego proponuję to jako doskonałe rozwiązanie odporne na awarie. Po prostu skopiuj i wklej tę funkcję i użyj jej zamiast niej.

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

wierzę najprostsza odpowiedź na to konkretne pytanie byłoby użyć wersji atof() która przyjmuje parametr C lokalizacji:

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

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

_free_locale(plocale); 

To pozwala nie zadzieraj ze strumieni, czy globalny locale lub manipulowanie ciągiem w ogóle. Wystarczy utworzyć żądany obiekt ustawień narodowych, aby wykonać całe przetwarzanie, a następnie zwolnić go po zakończeniu.

+0

Nie ma takiego wariantu dla biblioteki środowiska wykonawczego Windows CE (o której mowa) – tomash

+0

Ups - dziękuję za poprawkę! – Raptormeat

Powiązane problemy