2009-08-26 6 views
18

Ich analysiere GPS-Statuseinträge in festen NMEA-Sätzen, wobei der Bruchteil der geografischen Minuten immer nach der Periode kommt. Bei Systemen, in denen das Gebietsschema ein Komma als Dezimaltrennzeichen definiert, ignoriert die Funktion atof die Periode und den ganzen Bruchteil.Gebietsunabhängig "atof"?

Was ist die beste Methode, um mit diesem Problem umzugehen? Lange/Breite Zeichenfolge in Zeichen-Array gespeichert, wenn es darauf ankommt.

Beispielcode:

m_longitude = atof((char *)pField); 

Wo

pField[] = "01000.3897"; 

plattformübergreifende Projekt, für Windows XP und CE zusammengestellt.

Kommentar zu Lösung:

akzeptierte Antwort ist eleganter, aber this Antwort (und Kommentare) lohnt sich auch als eine schnelle Lösung zu wissen,

+0

Können Sie uns ein paar Beispiele für die Daten geben, mit denen Sie arbeiten müssen? Es könnte uns helfen, eine bessere Lösung zu bieten. – suszterpatt

+0

m_longitude = atof ((char *) pField); wobei pField [] = "01000.3897"; Plattformübergreifendes Projekt, kompiliert für Windows XP und CE. – tomash

+0

Gibt es einen triftigen Grund, nicht strtod zu verwenden (welches die gleiche Eigenschaft wie locale hat, aber eine bessere Fehlerbehandlung bietet)? – AProgrammer

Antwort

15

könnten Sie immer verwenden (Modulo Fehlerprüfung):

#include <sstream> 
... 

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

istr >> longitude; 

Der Standard iostreams verwenden Sie die globale locale standardmäßig (die wiederum sollten den klassischen (US) locale initialisiert werden). Daher sollte das oben Genannte funktionieren, es sei denn, jemand hat zuvor das globale Gebietsschema auf etwas anderes geändert, selbst wenn Sie auf einer nicht-englischen Plattform laufen. Um absolut sicher zu sein, dass das gewünschte Gebietsschema verwendet wird, ein bestimmtes Gebietsschema erstellen und „durchtränken“ den Stroms mit diesem locale vor dem Lesen:

#include <sstream> 
#include <locale> 

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

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

Als Randbemerkung, habe ich in der Regel reguläre Ausdrücke verwendet, um zu validieren NMEA-Felder, extrahieren Sie die verschiedenen Teile des Feldes als Captures und konvertieren Sie dann die verschiedenen Teile mit der obigen Methode. Der Teil vor dem Dezimalpunkt in einem NMEA-Längengradfeld ist tatsächlich als "DDDMM.mmm .." formatiert, wobei DDD Grad, MM.mmm bis Minuten entspricht (aber ich nehme an, dass Sie das bereits wussten).

+0

Es verwendet das globale C++ - Gebietsschema. Wenn Sie das globale C++ - Gebietsschema ändern, ändert sich das C-Gebietsschema, wenn es einen Namen hat. Wenn es keine Auswirkungen auf das Gebietsschema C hat, ist die Implementierung definiert. – AProgrammer

+0

@AProgrammer: Haben Sie meine Antwort tatsächlich gelesen und verstanden, bevor Sie kommentieren/ablehnen? – rjnilsson

+0

@AProgrammer: Ok, meine Antwort noch einmal gelesen, es war vielleicht nicht sehr klar. Nichtsdestotrotz habe ich nie vorgeschlagen, das globale Gebietsschema zu ändern. Ich habe nur erwähnt, dass, wenn es jemand anderes getan hat, dies Auswirkungen auf den Beispielcode haben wird. – rjnilsson

6

Eine böse Lösung, die ich einmal gemacht habe, um sprintf() ist 0.0f und schnappe das zweite Zeichen von der Ausgabe. Dann ersetzen Sie in der Eingabezeichenfolge '.' durch diesen Charakter. Dies löst den Komma-Fall, würde aber auch funktionieren, wenn ein Gebietsschema andere Dezimaltrennzeichen definiert.

+4

localeconv (in ) gibt einen Zeiger auf die Struktur zurück, deren Dezimalpunkt-Element diesen Wert enthält. Beachten Sie, dass der Zeiger bis zum nächsten localeconv() oder setlocale() gültig ist – AProgrammer

2

Jeder Grund, warum Sie nicht ein setlocale "C" vor dem Atof tun und das Gebietsschema danach wiederherstellen können? Vielleicht falsch verstanden habe ich die Frage ...

+0

Definitiv. Ich kann keine Auswirkungen auf andere Teile des Systems riskieren, und das Ändern des Gebiets kann sicher andere Prozesse beeinflussen. – tomash

+1

Der Setlocale-Aufruf wirkt sich nur auf das Gebietsschema des aktuellen Prozesses aus. Wenn Sie andere Threads haben, die Gebietsschema-abhängige Dinge tun, müssten sie synchronisiert werden. – danio

+0

AFAIK unter Windows CE Ländereinstellungen sind global, nicht pro Prozess repliziert – tomash

0

Sie können alle Zeichen in dem Array durchlaufen und tauschen alle Nicht-Zahlen mit einem . Charakter, der so lange funktionieren sollte, wie die Koordinaten in einem number-single_delimiter_character_-number-Format sind.

+0

Misundrestanding. Es wird immer eine einzelne Periode geben, aber manchmal erwartet atof ein Komma und ignoriert einen Bruchteil nach dem anderen. – tomash

+0

Rechts. In diesem Fall würde ich mit MSalters Lösung gehen: Drucken Sie ein Float, holen Sie das Trennzeichen, dann ersetzen Sie das '.' Damit. – suszterpatt

0

Müssen Sie wirklich Gebietsschema für Zahlen erhalten? Wenn nicht

setlocale(LC_ALL|~LC_NUMERIC, ""); 

oder die äquivalente Verwendung von std :: locale-Konstruktor.

0

Einige der oben genannten Lösungen schienen nicht zu funktionieren, daher schlage ich dies als eine absolut ausfallsichere Lösung vor. Kopieren Sie einfach diese Funktion und verwenden Sie sie stattdessen.

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

Ich glaube, die einfachste Antwort auf diese spezielle Frage die Version von atof() zu verwenden wäre, die eine C locale Parameter nimmt:

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

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

_free_locale(plocale); 

Auf diese Weise können Sie mit Streams nicht verwirren, oder die globale Gebietsschema oder mit dem Manipulieren der Zeichenfolge überhaupt. Erstellen Sie einfach das gewünschte Gebietsschemaobjekt, mit dem Sie Ihre gesamte Verarbeitung durchführen können, und geben Sie es anschließend frei, wenn Sie fertig sind.

+0

Keine solche Variante für Windows CE Runtime-Bibliothek (erwähnt in Frage) – tomash

+0

Hoppla - danke für die Korrektur! – Raptormeat