c++ - autolisp - Locale-independiente "atof"?
atof autolisp (7)
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 período. Sin embargo, en sistemas donde la configuración regional define una coma como separador decimal, la función atof ignora el período y la parte de la fracción completa.
¿Cuál es el mejor método para tratar este problema? Cadena larga / latitud almacenada en la matriz de caracteres, si es importante.
Código de ejemplo:
m_longitude = atof((char *)pField);
Dónde
pField[] = "01000.3897";
Proyecto multiplataforma, compilado para Windows XP y CE.
Comentario a la solución:
La respuesta aceptada es más elegante, pero this respuesta (y comentario) también vale la pena conocerla como una solución rápida
¿Alguna razón por la que no puede hacer un setlocale "C" antes de la instalación y restaurar la configuración regional después? Tal vez entendí mal la pregunta ...
¿Realmente necesitas obtener un comportamiento local para los valores numéricos? Si no
setlocale(LC_ALL|~LC_NUMERIC, "");
o el uso equivalente de std :: locale constructor.
Algunas de las soluciones anteriores no parecían funcionar, por lo que propongo esto como una solución perfectamente a prueba de fallas. Solo copia y pega esta función y úsala 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;
}
Creo que la respuesta más simple a esta pregunta específica sería usar la versión de atof()
que toma un parámetro de configuración regional de C:
_locale_t plocale = _create_locale( LC_ALL, "C" );
double result = _atof_l( "01000.3897", plocale );
_free_locale( plocale );
Esto le permite no meterse con las secuencias, ni con la configuración regional global, o con la manipulación de la cadena, en absoluto. Simplemente cree el objeto de configuración regional deseado para realizar todo el procesamiento y luego libérelo cuando haya terminado.
Puede recorrer todos los caracteres de la matriz e intercambiar los no números con a .
carácter, que debería funcionar siempre y cuando las coordenadas estén en un formato number-single_delimiter_character_-number
.
Siempre se puede usar (modulo error-control):
#include <sstream>
...
float longitude = 0.0f;
std::istringstream istr(pField);
istr >> longitude;
Los iostreams estándar utilizan la configuración regional global de forma predeterminada (que a su vez debe inicializarse a la configuración regional clásica (EE.UU.)). Por lo tanto, lo anterior debería funcionar en general, a menos que alguien haya cambiado previamente la configuración regional a otra cosa, incluso si está ejecutando en una plataforma no inglesa. Para estar absolutamente seguro de que se utiliza la configuración regional deseada, cree una configuración regional específica e "imbuye" la secuencia con esa configuración regional antes de leerla:
#include <sstream>
#include <locale>
...
float longitude = 0.0f;
std::istringstream istr(pField);
istr.imbue(std::locale("C"));
istr >> longitude;
Como nota al margen, usualmente he usado expresiones regulares para validar los campos NMEA, extraer las diferentes partes del campo como capturas y luego convertir las diferentes partes utilizando el método anterior. La parte anterior al punto decimal en un campo de longitud NMEA en realidad tiene el formato "DDDMM.mmm .." donde DDD corresponde a grados, MM.mmm a minutos (pero supongo que ya lo sabía).
Una solución desagradable que he hecho una vez es sprintf()
0.0f y tomar el segundo carácter de la salida. Luego, en la cadena de entrada, reemplace ''.'' por ese personaje. Esto resuelve el caso de coma, pero también funcionaría si una configuración regional definiera otros separadores decimales.