2009-08-26 15 views
18

Estoy analizando las entradas de estado de GPS en oraciones NMEA fijas, donde la fracción de minutos geográficos aparece siempre después del punto. Sin embargo, en sistemas donde la configuración regional define la coma como separador decimal, atof la función ignora el período y la parte de la fracción completa."atof" independiente de la configuración regional?

¿Cuál es el mejor método para solucionar este problema? Cadena larga/latitud almacenada en matriz de caracteres, si es importante.

Ejemplo de código:

m_longitude = atof((char *)pField); 

Dónde

pField[] = "01000.3897"; 

proyecto multiplataforma, compilado para Windows XP y CE.

comentario a la solución:

respuesta aceptada es más elegante, pero this respuesta (y comentarios) es también vale la pena conocer como una solución rápida

+0

¿Puede darnos algunos ejemplos de los datos con los que debe trabajar? Podría ayudarnos a brindar una mejor solución. – suszterpatt

+0

m_longitude = atof ((char *) pField); donde pField [] = "01000.3897"; Proyecto multiplataforma, compilado para Windows XP y CE. – tomash

+0

¿Existe alguna razón válida para no utilizar strtod (que tiene la misma característica sobre la configuración regional pero tiene un mejor manejo de errores)? – AProgrammer

Respuesta

15

Siempre se puede utilizar (módulo de comprobación de errores):

#include <sstream> 
... 

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

istr >> longitude; 

Los iostreams estándar utiliza la configuración regional mundial por defecto (que a su vez debe ser inicializado a la configuración regional clásica (US)). Por lo tanto, lo anterior debería funcionar en general a menos que alguien haya cambiado previamente la configuración global a otra cosa, incluso si está ejecutando en una plataforma que no sea en inglés. Para estar absolutamente seguro de que el local que necesita se utiliza, crear una configuración regional específica y "imbuir" la corriente con ese lugar antes de la lectura de ella:

#include <sstream> 
#include <locale> 

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

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

Como nota al margen, he utilizado generalmente expresiones regulares para validar Campos NMEA, extrae las diferentes partes del campo como capturas y luego convierte las diferentes partes utilizando el método anterior. La porción antes del punto decimal en un campo de longitud NMEA en realidad está formateada como "DDDMM.mmm .." donde DDD corresponde a grados, MM.mmm a minutos (pero supongo que ya lo sabía).

+0

Utiliza la configuración regional global de C++. La modificación de la configuración regional global de C++ modifica la configuración regional de C si tiene un nombre; si no tiene el efecto sobre la configuración regional de C, se define la implementación. – AProgrammer

+0

@AProgrammer: ¿De verdad leíste y entendiste mi respuesta antes de comentar/downvoting? – rjnilsson

+0

@AProgrammer: Ok, volver a leer mi respuesta podría no haber sido muy claro. Sin embargo, nunca sugerí cambiar el escenario global, solo mencioné que si alguien más lo hizo, tendrá efecto en el código de muestra. – rjnilsson

6

Una solución desagradable que he hecho una vez es sprintf() 0.0f y toma el segundo caracter de la salida. Luego, en la cadena de entrada, reemplaza '.' por ese personaje. Esto resuelve el caso de la coma, pero también funcionaría si una configuración regional definiera otros separadores decimales.

+4

localeconv (en ) devuelve un puntero a struct cuyo miembro decimal_point contiene ese valor. Tenga en cuenta que el puntero es válido hasta el próximo localeconv() o setlocale() – AProgrammer

2

¿Alguna razón por la que no puede hacer un setlocale "C" antes del atof y restaurar la configuración regional después? Tal vez malinterpreté la pregunta ...

+0

Definitivamente. No puedo arriesgar ningún impacto en otras partes del sistema y cambiar la configuración regional seguramente puede afectar a otros procesos. – tomash

+1

la llamada setlocale solo afecta la configuración regional del proceso actual. Si tiene otros hilos que hacen cosas que dependen de la configuración regional, necesitarían estar sincronizados. – danio

+0

AFAIK en Windows Las configuraciones regionales de CE son globales, no se replican por proceso – tomash

0

Podría recorrer todos los caracteres de la matriz e intercambiar los que no son números con un ., que debería funcionar siempre que las coordenadas estén en un formato number-single_delimiter_character_-number.

+0

Merece la pena destacar. Siempre habrá un período único, pero a veces atof esperará una coma e ignorará una fracción tras otra. – tomash

+0

Derecha. En ese caso, iría con la solución de MSalters: imprimir un flotador, obtener el delimitador, luego reemplazar el '.' con él. – suszterpatt

0

¿Realmente necesita obtener el comportamiento de localización para los números? Si no es

setlocale(LC_ALL|~LC_NUMERIC, ""); 

o el uso equivalente del constructor std :: locale.

0

Algunas de las soluciones anteriores no parecían funcionar, por lo que propongo esto como una solución perfectamente a prueba de fallas. Simplemente copie y pegue esta función y utilícela en su lugar.

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

Creo que la respuesta más simple a esta pregunta específica sería el uso de la versión de atof() que toma un parámetro C locale:

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

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

_free_locale(plocale); 

Esto le permite no meterse con corrientes o mundial locale, o con la manipulación de la cadena, en absoluto. Simplemente crea el objeto de configuración regional deseado para hacer todo tu procesamiento y luego libéralo cuando hayas terminado.

+0

No existe tal variante para la biblioteca de tiempo de ejecución de Windows CE (mencionado en la pregunta) – tomash

+0

Vaya, gracias por la corrección. – Raptormeat

Cuestiones relacionadas