与sscanf的双重阅读时忽略'E' [英] Ignore 'E' when reading double with sscanf

查看:102
本文介绍了与sscanf的双重阅读时忽略'E'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have input such as "(50.1003781N, 14.3925125E)" .These are latitude and longitude.

我想与解析这个

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

%LF 看到电子的号码后,它消耗它,并将其存储在指数形式号。有没有方法来禁用此?

but when %lf sees E after the number, it consumes it and stores it as number in exponential form. Is there way to disable this?

推荐答案

我认为你需要做的手工解析,可能使用关于strtod()。这说明关于strtod()行为三立当对尾随上来电子(至少在Mac OS X 10.10 .3 GCC 4.9.1 - 但可能无处不在)

I think you'll need to do manual parsing, probably using strtod(). This shows that strtod() behaves sanely when it comes up against the trailing E (at least on Mac OS X 10.10.3 with GCC 4.9.1 — but likely everywhere).

#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() A号,查为 N 取值或小写的版本中,逗号,关于strtod()一个数字,检查是W 电子,检查也许让之前的空白。

Basically, you'll skip white space, check for an (, strtod() a number, check for N or S or lower case versions, comma, strtod() a number, check for W or E, check for ) maybe allowing white space before it.

升级code,中度一般 strtolatlon根据()函数的strtod()等。在'常量投是在功能的必要,如关于strtod()内搭一件为const char * 输入和返回指针指向通过字符该字符串** EPTR 变量。

Upgraded code, with moderately general strtolatlon() function based on strtod() et al. The 'const cast' is necessary in the functions such as strtod() which take a const char * input and return a pointer into that string via a char **eptr variable.

#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!>>

这是的的COM prehensive测试,但它是说明性的,接近生产质量。你可能需要担心无穷大,例如,在真实的生产code。我不经常使用转到,但是这是在哪里使用转到简化错误处理的情况。你可以写在code离不开它;如果我有更多的时间,也许我会升级。然而,与七个地方的错误被诊断和报告错误所需的4条线,在转到提供合理的清晰度没有伟大重复。

That is not comprehensive testing, but it is illustrative and close to production quality. You might need to worry about infinities, for example, in true production code. I don't often use goto, but this is a case where the use of goto simplified the error handling. You could write the code without it; if I had more time, maybe I would upgrade it. However, with seven places where errors are diagnosed and 4 lines required for reporting the error, the goto provides reasonable clarity without great repetition.

请注意, strtolatlon()功能明确标识通过其返回值的错误;没有必要去猜测是否成功与否。如果你想找出错误所在,你可以增强错误报告。但这样做,取决于你的错误报告的基础设施的方式,这并不

Note that the strtolatlon() function explicitly identifies errors via its return value; there is no need to guess whether it succeeded or not. You can enhance the error reporting if you wish to identify where the error is. But doing that depends on your error reporting infrastructure in a way this does not.

此外, strtolatlon()函数将接受一些奇怪的球格式,如(+ 0.501003781E2N,143925125E-7E)。如果这是一个问题,你需要编写自己的关于strtod()只接受定点表示法的fussier变种。在另一方面,有一个米姆/准则在你接受什么样的大方;严格你生产。这意味着,什么是这里或多或少正常(这可能是件好事,让N,S,E之前可选空格,W字母,逗号和右括号)。反过来code, latlontostr() fmt_latlon()(用 strtolatlon( )改名 scn_latlon(),也许)或什么的,会是谨慎使用它产生,只有在生成大写字母,始终使用固定格式等。

Also, the strtolatlon() function will accept some odd-ball formats such as (+0.501003781E2N, 143925125E-7E). If that's a problem, you'll need to write your own fussier variant of strtod() that only accepts fixed-point notation. On the other hand, there's a meme/guideline "Be generous in what you accept; be strict in what you produce". That implies that what's here is more or less OK (it might be good to allow optional white space before the N, S, E, W letters, the comma and the close parenthesis). The converse code, latlontostr() or fmt_latlon() (with strtolatlon() renamed to scn_latlon(), perhaps) or whatever, would be careful about what it produces, only generating upper-case letters, and always using the fixed format, etc.

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;
}

请注意一个度的1个单位的7位(10 -7 ˚),大约相当于地面上的厘米(沿着子午线导向;由psented的距离再$ P $沿北纬度随纬度而异,当然)。

Note that 1 unit at 7 decimal places of a degree (10-7 ˚) corresponds to about a centimetre on the ground (oriented along a meridian; the distance represented by a degree along a parallel of latitude varies with the latitude, of course).

这篇关于与sscanf的双重阅读时忽略'E'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆