忽略' E'当用sscanf读取双倍

时间:2015-04-10 14:20:10

标签: c++ c scanf

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

我想用

解析这个问题
sscanf(string,"(%lf%c, %lf%c)",&a,&b,&c,&d);

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

3 个答案:

答案 0 :(得分:5)

我认为您需要进行手动解析,可能需要使用strtod()。这表明strtod()在遇到尾随E时表现得非常明显(至少在Mac OS X 10.10.3上使用GCC 4.9.1 - 但可能无处不在)。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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或小写版本,逗号,{ {1}}一个数字,请检查strtod()W,检查E是否允许前面有空格。

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

char **eptr

示例输出:

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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简化错误处理的情况。你可以在没有它的情况下编写代码;如果我有更多时间,也许我会升级它。但是,有七个诊断错误的位置和报告错误需要4行,goto提供了合理的清晰度而没有重复。

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

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

scn_latlon()

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

答案 1 :(得分:4)

首先使用

处理字符串
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标头<string.h>中声明。

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

答案 2 :(得分:0)

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