我有这样的输入:"(50.1003781N, 14.3925125E)"
。这是纬度和经度。
我想要使用
sscanf(string,"(%lf%c, %lf%c)",&a,&b,&c,&d);
但是当%lf
在数字后面看到E
时,它会将其消耗并将其存储为指数形式的数字。有没有办法禁用这个功能?
但是当%lf
在数字后面看到E
时,它会将其消耗并将其存储为指数形式的数字。有没有办法禁用这个功能?
strtod()
。这表明在处理结尾的 E
时,strtod()
的行为是合理的(至少在 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()
一个数字,检查N
或S
或小写版本,逗号,strtod()
一个数字,检查W
或E
,检查)
,可能在其前允许空格。strtod()
等的中等通用的strtolatlon()
函数。在诸如strtod()
之类的函数中,'const cast'是必要的,该函数接受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!>>
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;
}
首先使用以下方式处理字符串
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方法。
'W'
可能不是最好的选择。如果我要往西走怎么办? - QuentinE
或 e
更改为 W
,以便在使用 sscanf()
后可以撤消更改。您可能还想记录原始字符,以便可以将字符串恢复为原样。当然,这种方法也意味着您无法扫描包含纬度/经度的字符串文字。 - Jonathan Leffler你可以尝试读取整个字符串,然后用另一个字符替换 E。
E
,然后如果它有这个占位符,就将b
或d
替换为E
。 - NathanOlivergoto error;
语句跳过了int c = toupper((unsigned char)*end++);
- 次要问题是使用了C风格的转换,如(unsigned char)*end++
。) - Jonathan Leffler