【问题标题】:Sscanf delimiters for parsing用于解析的 Sscanf 分隔符
【发布时间】:2017-07-13 11:24:27
【问题描述】:

我想使用 sscanf 解析 /etc/passwd 文件。目前正在尝试低于 sn-p

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

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

【问题讨论】:

  • 你需要使用扫描集%[…],或者更准确地说是[^:],一个否定的扫描集。
  • 究竟如何?我有点困惑请帮助我
  • 如果您需要有关如何使用该功能的一般说明,网上有很多关于scanf 格式字符串如何工作和扫描集的文档。你应该尝试谷歌搜索它。如果您已阅读文档并且对您不理解的详细点有具体问题,请澄清您的问题。

标签: c scanf


【解决方案1】:

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

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

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

请注意,由于您使用的是sscanf(),因此实际上不需要处理任何尾随字段。此外,密码文件条目中有 7 个字段,而不是 6 个字段。使用sscanf(),这不是问题。如果您正在从文件中读取,那将是。此外,如果您正在从文件中读取,您将不得不担心扫描集不会跳过前导空格,这将是前一行输入留下的换行符。对于文件流解析,您需要使用:

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

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

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

输出:

User: [root]; GID = 1

(我破解了条目,所以 UID 和 GID 不同。)

fscanf()例子

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

请注意,在 Mac 上,密码文件以多行 # cmets 开头。 %[^:\n] 符号或类似符号是必需的,以避免解析文件的该部分时出现问题。在文件中没有此类注释行的健全系统上,您可能没有它们就可以逃脱。还要注意,代码会保护自己免受字符串字段的溢出。

此外,对于 UID 和 GID,我继续使用 unsigned 整数,但对于 UID 和 GID,nobody 具有负值 -2,因此带符号的类型可能会更好。

示例输出:

[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]
…

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

样本输出:

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

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

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes pw89.c -o pw89
$

【讨论】:

  • 获取垃圾---->“Netconf 用户:vbisalah22,相应组 ID 为:976499253 Netconf 用户:vbisalah23,相应组 ID 为:976564789”但这些用户的组 ID 为 495
  • 修复了 UID 扫描集中的错字。我的编译器告诉我我的错误。你没有告诉你我的错误吗?如果没有,请使用更好的编译器。
猜你喜欢
  • 1970-01-01
  • 2015-07-12
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
相关资源
最近更新 更多