C语言——每日一题(1)

单向链表保存/etc/passwd

使用单向链表保存/etc/passwd的内容,user结构体如下所示:

struct user {
    struct user *next;
    char *name;
    unsigned int uid;
    unsigned int gid;
    char home[64];
    char shell[64];
};

sscanf函数

sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。sscanf可用来进行数据类型或进制转换以及按特定规则读取源字符串的特定规则。
常见几种用法:

  1. %[abc]读取a、b、或c的任意一员,[a-z]表示匹配小写字母,[a-z0-9]表示匹配小写字母及数字
  2. %[^a-z]读取不在a-z之间的字符串,如果碰到 a-z 之间的字符则停止
  3. %[^=]读取字符串直到碰到=号
  4. %[^=]%s前面带号表示不保存变量。跳过字符串中以=结尾的子串而读后包括=在内的后面部分子串

在C语言中,sscanf 函数是用于从字符串中按照指定格式解析数据的函数。在格式字符串中,%m 和 %u 是两种格式化说明符,用于解析不同类型的数据。
1. %m 格式化说明符:
%m 用于解析一个字符串,并为其分配内存。它的用法类似于 %s,但是 %m 可以自动分配足够的内存来存储字符串,而不需要预先分配。这在避免内存溢出和缓冲区溢出问题方面非常有用。
在下面这个例子中,%m[^\n] 解析一行字符串,并为 output 分配足够的内存来存储它。

char *input = "Hello, world!";
char *output = NULL;
sscanf(input, "%m[^\n]", &output);
printf("Output: %s\n", output);
free(output);

2. %u 格式化说明符:
%u 用于解析无符号整数(unsigned int)。它期望在格式化字符串中匹配一个无符号整数,并将其解析为对应的无符号整数类型。
在这个例子中,字符串 “12345” 被解析为无符号整数值 12345。

unsigned int value;
sscanf("12345", "%u", &value);
printf("Value: %u\n", value);

答案解析:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<err.h>
#include<string.h>

#define BUF_SIZE 512

struct user* parse_user(char *str);

struct user {
    struct user *next;
    char *name;
    unsigned int uid;
    unsigned int gid;
    char home[64];
    char shell[64];
};

struct user* parse_user(char *str){
    struct user* new_user = (struct user*)malloc(sizeof(struct user));
    if(new_user == NULL) {
        err(EXIT_FAILURE, "malloc");
    }
    memset(new_user, 0, sizeof(*new_user));

    int flags = sscanf(str, "%m[^:]:%*[^:]:%u:%u:%*[^:]:%63[^:]:%63s",  \
                    &(new_user->name), &(new_user->uid), &(new_user->gid),new_user->home, new_user->shell) == 5
                | sscanf(str, "%m[^:]:%*[^:]:%u:%u::%63[^:]:%63s",     \
                    &(new_user->name), &(new_user->uid), &(new_user->gid),new_user->home, new_user->shell) == 5;
    if(flags){
        printf("%s\t %u\t %u\t %s\t %s\n", \
        new_user->name, new_user->uid, new_user->gid, new_user->home, new_user->shell);
        return new_user;
    }

    if(new_user && new_user->name) free(new_user->name);
    if(new_user) free(new_user);
    return NULL;
}

int main(int argc, char *argv[]){
    FILE* fp = fopen("/etc/passwd", "r");
    if(fp == NULL){
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    char buf[BUF_SIZE];
    struct user* head = NULL;
    struct user* cur  = NULL;

    while(fgets(buf, sizeof(buf), fp)){
        struct user* newNode = parse_user(buf);

        newNode->next = NULL;
        if(head == NULL){
            head = newNode;
            cur = newNode;
        }else{
            cur->next = newNode;
            cur = newNode;
        }
    }
    fclose(fp);
    cur = head;
    while(cur != NULL){
        cur = cur->next;
    }
    cur = head;
    while(cur != NULL){
        struct user* tmp = cur;
        cur = cur->next;
        free(tmp);
    }
    return 0;
}

自己写的答案,有什么不足请大佬们指正,加快学习进度。

欢迎关注知乎账号:勤奋的小牛

勤奋的小牛

C语言–每日一题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值