【问题标题】:Ignore 'E' when reading double with sscanf使用 sscanf 读取双精度时忽略“E”
【发布时间】:2015-06-16 07:33:58
【问题描述】:

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

我想用

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

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

【问题讨论】:

  • 一种选择是将输入字符串分解为子字符串并从那里扫描。
  • 您可以将E 替换为占位符值,然后将bd 更改为E(如果有占位符)。
  • 你能移动到所有的经度都是W而不是E吗?
  • 不确定我是否理解你的问题,我会在稍后读取这些坐标,如果它的 W i 存储为负双精度,如果它的 E il lstore ti 为正数
  • 如果您不确定我的问题,请高枕无忧。这主要是一个笑话。您可能无法假装东经不存在。

标签: c++ c scanf


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    首先使用处理字符串

    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 方法。

    【讨论】:

    • 谢谢,c++ 方法是什么?这是 c++ 项目的一部分,我只是想不出任何其他想法然后尝试使用 sscanf 来解决这个问题
    • 使用'W' 可能不是最好的主意。如果我要去西部呢?
    • 至少需要记录一下你是把E还是e改成了W,这样你在使用sscanf()之后就可以撤销修改了。您可能想要记录原始字符,以便将字符串恢复原状。当然,这种方法也意味着您无法扫描包含纬度/经度的字符串文字。
    • 是的,好的。重点是更改字符以进行扫描,并记录足够的信息以便检索值(例如,如果更改 E-W,则乘以 -1)。有效坐标不会有不同的东坐标和西坐标。在尝试扫描坐标之前和之后都需要进行一整套错误检查(检查字符串仅包含一对坐标,检查 sscanf() 的返回等)。
    • C++ 方法会使用 std::string 类型(并且可能不会将变量命名为“字符串”)。 std::string 支持各种操作来替换子字符串等。对于扫描/解析(而不是 sscanf()),一种方法是将字符串提供给 std::istringstream 并使用它来提取值。
    【解决方案3】:

    我认为您需要手动解析,可能使用strtod()。这表明strtod() 在遇到尾随的E 时表现良好(至少在带有 GCC 4.9.1 的 Mac OS X 10.10.3 上——但可能无处不在)。

    #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 或小写版本、逗号、strtod() 一个数字、检查@ 987654332@ 或 E,检查 ) 可能在其前面允许空格。

    基于strtod() 等人的升级代码,具有适度通用的strtolatlon() 功能。 'const cast' 在诸如 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,但这是使用goto 简化错误处理的情况。没有它你也可以编写代码;如果我有更多的时间,也许我会升级它。但是,由于有 7 个地方可以诊断出错误,并且需要 4 行来报告错误,goto 提供了合理的清晰度,而没有过多的重复。

    请注意,strtolatlon() 函数通过其返回值显式识别错误;成功与否无需猜测。如果您希望确定错误在哪里,您可以增强错误报告。但这样做取决于您的错误报告基础架构,而这并非如此。

    此外,strtolatlon() 函数将接受一些奇数球格式,例如(+0.501003781E2N, 143925125E-7E)。如果这是一个问题,您需要编写自己的 strtod() 更复杂的变体,它只接受定点表示法。另一方面,有一个meme/guideline“对你接受的东西要慷慨;对你生产的东西要严格”。这意味着这里的内容或多或少是可以的(在 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 位的 1 个单位 (10-7 ˚) 对应于地面上大约一厘米(沿子午线方向;沿平行线的度数表示的距离)当然,纬度是随纬度而变化的)。

    【讨论】:

      猜你喜欢
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 2013-12-28
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 2023-03-13
      相关资源
      最近更新 更多