用于解析的Sscanf分隔符

时间:2017-07-13 11:24:27

标签: c scanf

我想使用sscanf解析/ etc / passwd文件。目前正在尝试下面的代码段

        sscanf(buf,"%s:%*s:%*u:%*u:%*s:%*s",
               szName, &ncUser_gid);

错了。我只需要解析该用户的用户名和相应的组ID

1 个答案:

答案 0 :(得分:3)

基本答案是使用(否定)'扫描集' - 阅读sscanf()的手册。

if (sscanf(buf, "%[^:]:%*[^:]:%*[^:]:%u", szName, &ncUser_gid) != 2)

将非冒号序列读入szName,然后跳过冒号,密码字段,冒号,UID字段,冒号,并将下一个字段中的数字读入{{1 }}。它还会检查您是否同时获得了两个值,而忽略了其他尾随字段(comment,home,shell)。

请注意,因为您使用的是ncUser_gid,所以根本不需要处理任何尾随字段。此外,密码文件条目中有7个字段,而不是6个字段。使用sscanf(),这不是问题。如果您正在阅读文件,那就是。此外,如果您正在读取文件,则必须担心扫描集不会跳过前导空格,这将是前一行输入中遗留的换行符。对于文件流解析,您需要使用:

sscanf()

请注意,shell字段不必有任何数据。这也会导致空的注释字段,但通常会填充。请注意,它跳过了密码;它在Unix的现代版本中很少有用。

int rc; if ((rc = fscanf(fp, " %[^:]:%*[^:]:%u:%u:%[^:]:%[^:]:%[^:]", username, &uid, &gid, comment, homedir, shell)) != 5 && rc != 6) …handle format error… if (rc == 5) shell[0] = '\0'; 示例

sscanf()

输出:

#include <stdio.h>

int main(void)
{
    char buf[] = "root:*:0:1:System Administrator:/var/root:/bin/sh";

    char szName[10] = "Pygmalion";      // Make sure it isn't empty!;
    unsigned int ncUser_gid = 23456;    // Make sure it isn't zero!
    if (sscanf(buf, "%[^:]:%*[^:]:%*[^:]:%u", szName, &ncUser_gid) != 2)
        printf("Ooops!\n");
    else
        printf("User: [%s]; GID = %u\n", szName, ncUser_gid);
    return 0;
}

(我攻击了该条目,因此UID和GID不同。)

User: [root]; GID = 1 示例

fscanf()

请注意,在Mac上,密码文件以多行#include <stdio.h> int main(void) { const char passwd[] = "/etc/passwd"; FILE *fp = fopen(passwd, "r"); if (fp == 0) { fprintf(stderr, "failed to open '%s' for reading\n", passwd); return 1; } char username[64]; unsigned uid; unsigned gid; char comment[64]; char homedir[64]; char shell[64]; int rc; while (!feof(fp)) { if ((rc = fscanf(fp, " %63[^:\n]:%*[^:\n]:%u:%u:%63[^:\n]:%63[^:\n]:%63[^:\n]", username, &uid, &gid, comment, homedir, shell)) != 5 && rc != 6) { int c; while ((c = getc(fp)) != EOF && c != '\n') ; } else { if (rc == 5) shell[0] = '\0'; printf("[%s] %u %u [%s] [%s] [%s]\n", username, uid, gid, comment, homedir, shell); } } return 0; } 条评论开头。需要#表示法或类似的表示法来避免解析文件的该部分时出现问题。在没有文件中的这些注释行的理智系统上,没有它们,你可能会离开。另请注意,代码可以保护自己免受字符串字段中的溢出。

此外,我继续使用%[^:\n]整数表示UID和GID,但unsigned对UID和GID都有负值nobody,因此签名类型可能更好。

示例输出:

-2

所有用户都在我的Mac上指定了特定的shell,因此'rc == 5'代码尚未经过测试。

示例输出:

[nobody] 4294967294 4294967294 [Unprivileged User] [/var/empty] [/usr/bin/false]
[root] 0 0 [System Administrator] [/var/root] [/bin/sh]
[daemon] 1 1 [System Services] [/var/root] [/usr/bin/false]
…

JFTR:使用GCC 7.1.0在运行macOS 10.12.5的Mac上测试。 编译命令行如:

[# Open Directory.
##
nobody] 4294967294 4294967294 [Unprivileged User] [/var/empty] [/usr/bin/false