用sscanf读取双倍时忽略’E’

我输入了"(50.1003781N, 14.3925125E)" 。这些是纬度和经度。

我想解析这个

 sscanf(string,"(%lf%c, %lf%c)",&a,&b,&c,&d); 

但是当%lf在数字后面看到E ,它会消耗它并以指数forms将其存储为数字。 有没有办法禁用它?

我想你需要手动解析,可能使用strtod() 。 这表明strtod()在遇到尾随E时表现得非常好(至少在Mac OS X 10.10.3上使用GCC 4.9.1 – 但可能到处都是)。

 #include  #include  #include  #include  int main(void) { const char latlong[] = "(50.1003781N, 14.3925125E)"; char *eptr; double d; errno = 0; // Necessary in general, but probably not necessary at this point d = strtod(&latlong[14], &eptr); if (eptr != &latlong[14]) printf("PASS: %10.7f (%s)\n", d, eptr); else printf("FAIL: %10.7f (%s) - %d: %s\n", d, eptr, errno, strerror(errno)); return 0; } 

编译和运行:

 $ gcc -O3 -g -std=c11 -Wall -Wextra -Werror latlong.c -o latlong $ ./latlong PASS: 14.3925125 (E)) $ 

基本上,你将跳过空格,检查(strtod()数字,检查NS或小写版本,逗号, strtod()数字,检查WE ,检查)也许允许白色它之前的空间。

升级代码,具有基于strtod()等的中等一般strtolatlon()函数。 ‘const cast’在诸如strtod()类的函数中是必需的,它接受const char *输入并通过char **eptr变量将指针返回到该字符串。

 #include  #include  #include  #include  #include  #define CONST_CAST(type, value) ((type)(value)) extern int strtolatlon(const char *str, double *lat, double *lon, char **eptr); int strtolatlon(const char *str, double *lat, double *lon, char **eptr) { const char *s = str; char *end; while (isspace(*s)) s++; if (*s != '(') goto error; *lat = strtod(++s, &end); if (s == end || *lat > 90.0 || *lat < 0.0) goto error; int c = toupper((unsigned char)*end++); if (c != 'N' && c != 'S') // I18N goto error; if (c == 'S') *lat = -*lat; if (*end != ',') goto error; s = end + 1; *lon = strtod(s, &end); if (s == end || *lon > 180.0 || *lon < 0.0) goto error; c = toupper((unsigned char)*end++); if (c != 'W' && c != 'E') // I18N goto error; if (c == 'E') *lon = -*lon; if (*end != ')') goto error; if (eptr != 0) *eptr = end + 1; return 0; error: if (eptr != 0) *eptr = CONST_CAST(char *, str); errno = EINVAL; return -1; } int main(void) { const char latlon1[] = "(50.1003781N, 14.3925125E)"; const char latlon2[] = " (50.1003781N, 14.3925125E) is the position!"; char *eptr; double d; errno = 0; // Necessary in general, but Probably not necessary at this point d = strtod(&latlon1[14], &eptr); if (eptr != &latlon1[14]) printf("PASS: %10.7f (%s)\n", d, eptr); else printf("FAIL: %10.7f (%s) - %d: %s\n", d, eptr, errno, strerror(errno)); printf("Converting <<%s>>\n", latlon2); double lat; double lon; int rc = strtolatlon(latlon2, &lat, &lon, &eptr); if (rc == 0) printf("Lat: %11.7f, Lon: %11.7f; trailing material: <<%s>>\n", lat, lon, eptr); else printf("Conversion failed\n"); return 0; } 

样本输出:

 PASS: 14.3925125 (E)) Converting << (50.1003781N, 14.3925125E) is the position!>> Lat: 50.1003781, Lon: -14.3925125; trailing material: << is the position!>> 

不是全面的测试,但它是说明性的,接近生产质量。 您可能需要担心无穷大,例如,在真正的生产代码中。 我不经常使用goto ,但这是使用goto简化了error handling的情况。 你可以在没有它的情况下编写代码; 如果我有更多时间,也许我会升级它。 但是,有七个诊断错误的位置和报告错误需要4行, goto提供了合理的清晰度而没有很大的重复。

请注意, strtolatlon()函数通过其返回值显式标识错误; 没有必要猜测它是否成功。 如果要确定错误的位置,可以增强错误报告。 但这样做取决于您的错误报告基础设施,而不是这样。

此外, strtolatlon()函数将接受一些奇数球格式,如(+0.501003781E2N, 143925125E-7E) 。 如果这是一个问题,你需要编写自己的strtod()函数变体,它只接受定点表示法。 另一方面,有一个模因/指导方针 “对你所接受的东西慷慨;对你所生产的东西要严格”。 这意味着这里的内容或多或少都可以(在N,S,E,W字母,逗号和右括号之前允许可选的空格可能是好的)。 相反的代码, latlontostr()fmt_latlon()strtolatlon()重命名为scn_latlon() )或其他什么,会小心它产生什么,只生成大写字母,并始终使用固定格式等。

 int fmt_latlon(char *buffer, size_t buflen, double lat, double lon, int dp) { assert(dp >= 0 && dp < 15); assert(lat >= -90.0 && lat <= 90.0); assert(lon >= -180.0 && lon <= 180.0); assert(buffer != 0 && buflen != 0); char ns = 'N'; if (lat < 0.0) { ns = 'S'; lat = -lat; } char ew = 'W'; if (lon < 0.0) { ew = 'E'; lon = -lon; } int nbytes = snprintf(buffer, buflen, "(%.*f%c, %.*f%c)", dp, lat, ns, dp, lon, ew); if (nbytes < 0 || (size_t)nbytes >= buflen) return -1; return 0; } 

请注意,在一个度数的7个小数位(10-7˚)的1个单位对应于地面上的大约1厘米(沿子午线定向;当然,沿纬度平行度表示的距离随纬度而变化) 。

首先使用处理字符串

 char *p; while((p = strchr(string, 'E')) != NULL) *p = 'W'; while((p = strchr(string, 'e')) != NULL) *p = 'W'; // scan it using your approach sscanf(string,"(%lf%c, %lf%c)",&a,&b,&c,&d); // get back the original characters (converted to uppercase). if (b == 'W') b = 'E'; if (d == 'W') d = 'E'; 

strchr()在C头

注意:这实际上是一种C方法,而不是C ++方法。 但是,通过使用sscanf()您实际上正在使用C方法。

您可以尝试读取所有字符串,然后将E替换为另一个字符