从用户中提取格式化的输入

Nor*_*her 1 c

我有一些遵循这种格式的用户输入:

Playa Raco#path#5#39.244|-0.257#0-23

#这里作为一个分隔符,而|也是纬度和经度的分隔符。我想提取这些信息。请注意,字符串可以有空格。我尝试使用%[^\n]%*c格式化程序scanf并添加#|,但它不起作用,因为它匹配整行。

我想尽可能简单,我知道我可以阅读每个字符,但我很想看看最佳实践并检查是否有一个scanf或类似的替代方案。

Dav*_*ica 5

正如评论中提到的,有很多方法可以解析字符串中的信息。您可以沿着字符串遍历一对指针,测试每个字符并采取适当的操作,您可以使用strtok(),但注意strtok()修改原始字符串,因此它不能用于字符串文字,您可以sscanf()用来解析来自的值字符串,或者您可以使用、、 等的任意组合strcspn(),然后手动复制开始和结束指针之间的每个字段。strspn()strchr()

但是,您的问题也强加了“我想尽可能简单地...”并且直接指向sscanf(). 您只需要验证退货即可。例如,你可以这样做:

#include <stdio.h>

#define MAXC 16     /* adjust as necessary */

int main (void) {
    
    const char *str = "Playa Raco#path#5#39.244|-0.257#0-23";
    char name[MAXC], path[MAXC], last[MAXC];
    int num;
    double lat, lon;
    
    if (sscanf (str, "%15[^#]#%15[^#]#%d#%lf|%lf#%15[^\n]",
                name, path, &num, &lat, &lon, last) == 6) {
        printf ("name : %s\npath : %s\nnum  : %d\n"
                "lat  : %f\nlon  : %f\nlast : %s\n",
                name, path, num, lat, lon, last);
    }
    else
        fputs ("error: parsing values from str.\n", stderr);
}
Run Code Online (Sandbox Code Playgroud)

注:%[..]转换不消耗前导空白,因此,如果有的前导空白或以下的空间中的可能性'#'的字符串转换前,包括在格式字符串的空间中,例如" %15[^#]# %15[^#]#%d#%lf|%lf# %15[^\n]"

其中要拆分的输入的每个字符串部分都声明为16字符数组。查看格式字符串,您会注意到每个字符串的读取仅限于15字符(加上空终止字符),以确保您不会尝试存储比数组可以容纳的更多字符。(这将调用Undefined Behavior)。由于请求了六次转换,您可以通过确保返回为 来验证转换6

示例使用/输出

采用这种方法,上面的输出将是:

./bin/parse_sscanf
name : Playa Raco
path : path
num  : 5
lat  : 39.244000
lon  : -0.257000
last : 0-23
Run Code Online (Sandbox Code Playgroud)

只要您验证转换并保护填充的任何字符数组的数组边界,就没有一种方法一定比另一种“更好”。但是,尽可能简单,这里很难被击败sscanf()——而且它不会修改您的原始字符串,因此与字符串文字一起使用是安全的。