题目要求
解析/etc/passwd文件内容,并保存到数组中,按照用户名和uid分别进行排序。
struct user {
char *name;
unsigned int uid;
unsigned int gid;
char home[64];
char shell[64];
};
与前一天的每日一题,非常相似,解析过程基本相同。
C语言——每日一题(1)
咱们来看代码过程,经过了多次版本的优化,感觉还是进步了那么一点点。
1. 数组操作初始版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#define BUF_SIZE 256
static int compareByUID(const void *, const void *);
static int compareByName(const void *, const void *);
struct user {
char name[100];
unsigned int uid;
}user;
int compareByName(const void *a, const void *b) {
return strcmp(((struct user *)a)->name, ((struct user *)b)->name);
}
int compareByUID(const void *a, const void *b) {
return ((struct user *)a)->uid - ((struct user *)b)->uid;
}
int parse_user(char* str, struct user *new_user){
int flags = sscanf(str, "%[^:]:%*[^:]:%u", new_user->name, &new_user->uid);
if(flags == 2){
return 0;
}
return -1;
}
int main(int argc, char* argv[]){
FILE *fp = fopen("passwd.txt", "r");
if(fp == NULL){
perror("open");
exit(EXIT_FAILURE);
}
struct user userInfo[100];
int userCount = 0;
char buf[BUF_SIZE];
while(fgets(buf, BUF_SIZE, fp)){
if(parse_user(buf, &userInfo[userCount++])){
printf("Parse users error\n");
exit(EXIT_FAILURE);
}
}
fclose(fp);
printf("old userInfo>>>>>>>>>\n");
for(int i = 0;i < userCount; i++){
printf("Name: %s, Uid: %d\n", userInfo[i].name, userInfo[i].uid);
}
printf("========================================\n");
printf("qsortByUID userInfo>>>>>>>>>\n");
qsort(userInfo, userCount, sizeof(struct user), compareByUID);
for(int i = 0;i < userCount; i++){
printf("Name: %s, Uid: %d\n", userInfo[i].name, userInfo[i].uid);
}
printf("========================================\n");
printf("qsortByName userInfo>>>>>>>>>\n");
qsort(userInfo, userCount, sizeof(struct user), compareByName);
for(int i = 0;i < userCount; i++){
printf("Name: %s, Uid: %d\n", userInfo[i].name, userInfo[i].uid);
}
exit(EXIT_SUCCESS);
}
2. 指针操作版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#define BUF_SIZE 1024
static int compareByUID(const void *, const void *);
static int compareByName(const void *, const void *);
struct user *parse_user(char* str);
void qSort_type(struct user **userInfo, int userCount, int compareUID, int compareName);
void free_user(struct user **userInfo, int userCount);
struct user {
char name[100];
unsigned int uid;
}user;
int compareByName(const void *a, const void *b) {
return strcmp((*(struct user **)a)->name, (*(struct user **)b)->name);
}
int compareByUID(const void *a, const void *b) {
return (*(struct user **)a)->uid - (*(struct user **)b)->uid;
}
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, "%[^:]:%*[^:]:%u", new_user->name, &new_user->uid);
if(flags != 2){
printf("error %d parsed", flags);
printf("Error str: %s\n",str);
}
return new_user;
}
int main(int argc, char* argv[]){
FILE *fp = fopen("passwd.txt", "r");
if(fp == NULL){
perror("open");
exit(EXIT_FAILURE);
}
struct user **userInfo = NULL;
int userCount = 0;
char buf[BUF_SIZE];
while(fgets(buf, BUF_SIZE, fp)){
struct user *new_user = parse_user(buf);
userInfo = (struct user **)realloc(userInfo, (userCount + 1) * sizeof(struct user *));
if(userInfo == NULL){
err(EXIT_FAILURE, "realloc");
}
userInfo[userCount++] = new_user;
}
fclose(fp);
printf("old userInfo>>>>>>>>>\n");
qSort_type(userInfo,userCount,0, 0);
printf("========================================\n");
printf("qsortByName userInfo>>>>>>>>>\n");
qSort_type(userInfo,userCount, 1, 0);
printf("========================================\n");
printf("qsortByUID userInfo>>>>>>>>>\n");
qSort_type(userInfo,userCount, 0, 1);
free_user(userInfo,userCount);
exit(EXIT_SUCCESS);
}
void qSort_type(struct user **userInfo, int userCount, int compareUID, int compareName){
for(int i = 0; i < userCount; i++){
if(!compareUID && compareName){
qsort(userInfo, userCount, sizeof(struct user *), compareByName);
}else if(compareUID && !compareName){
qsort(userInfo, userCount, sizeof(struct user *), compareByUID);
}
printf("Name: %s, Uid: %d\n", userInfo[i]->name, userInfo[i]->uid);
}
}
void free_user(struct user **userInfo, int userCount){
for(int i = 0;i < userCount; i++){
free(userInfo[i]);
}
free(userInfo);
}
3. 数组优化版本1
- 采用参数传递方式实现不同顺序
- 默认不加参数,则输出原序内容
- -n 按照name排序
- -d 按照uid排序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<err.h>
#include<string.h>
#define BUF_SIZE 512
int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
static int parse_user(char* str, struct user* new_user);
int commpare(const void *a, const void *b);
void qsort_type(int userCount);
void user_core(void);
struct user {
char *name;
unsigned int uid;
unsigned int gid;
char home[64];
char shell[64];
};
enum sort_type {
SORT_NONE=0,
SORT_NAME=1,
SORT_UID
};
static struct user userInfo[100];
static enum sort_type sort_type;
static int parse_user(char* str, struct user* new_user){
memset(new_user, 0, sizeof(*new_user));
if (sscanf(str, "%m[^:]:%*[^:]:%u:%u:%*[^:]:%63[^:]:%63s", \
&(new_user->name), &(new_user->uid), &(new_user->gid),new_user->home, new_user->shell) == 5){
return 0;
}else if(sscanf(str, "%m[^:]:%*[^:]:%u:%u::%63[^:]:%63s", \
&(new_user->name), &(new_user->uid), &(new_user->gid),new_user->home, new_user->shell) == 5){
return 0;
}
if(new_user && new_user->name) free(new_user->name);
if(new_user) free(new_user);
return -1;
}
int commpare(const void *a, const void *b){
switch (sort_type)
{
case SORT_NAME:
return strcmp(((struct user*)a)->name, ((struct user*)b)->name);
break;
case SORT_UID:
return ((struct user*)a)->uid - ((struct user*)b)->uid;
break;
default:
break;
}
}
void qsort_type(int userCount){
if(sort_type != SORT_NONE){
qsort(userInfo, userCount, sizeof(struct user), commpare);
}
return;
}
void user_core(void){
FILE* fp = fopen("passwd.txt", "r");
if(fp == NULL){
perror("fopen");
exit(EXIT_FAILURE);
}
char buf[BUF_SIZE];
int userCount = 0;
while(fgets(buf, sizeof(buf), fp)){
if(parse_user(buf, &userInfo[userCount++])){
printf("parse_user error\n");
exit(EXIT_FAILURE);
}
}
fclose(fp);
qsort_type(userCount);
printf("name\t uid\t gid\t home\t shell\n");
for(int i = 0;i < userCount; i++){
printf("%s\t %u\t %u\t %s\t %s\n", \
userInfo[i].name, userInfo[i].uid, userInfo[i].gid,userInfo[i].home, userInfo[i].shell);
}
for(int i = 0;i < userCount; i++){
if(userInfo[i].name) free(userInfo[i].name);
}
}
int main(int argc, char *argv[]){
sort_type = SORT_NONE;
int opt;
while((opt = getopt(argc, argv, "nd")) != -1){
switch (opt)
{
case 'n':
sort_type = SORT_NAME;
break;
case 'd':
sort_type = SORT_UID;
break;
default:
(void)fprintf(stderr, "Usage: %s [-nd] \n",argv[0]);
exit(EXIT_FAILURE);
}
}
// printf("optind = %d\n", optind);
if(optind > argc){
fprintf(stderr, "Expected argument after options\n");
exit(EXIT_FAILURE);
}
user_core();
return 0;
}
4. 数组继续优化2
方法3采用数组固定分配内存,利用率低。
- 优化采用动态内存分配的方式完成
- 加入DEBUG以跟踪代码,方便调试
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<err.h>
#include<string.h>
#define BUF_SIZE 255
#define DEBUG
#ifdef DEBUG
#define DPRINTF(format,...) \
do { printf("%s::%s "format,__FILE__,__FUNCTION__,##__VA_ARGS__);}while(0)
#else
#define DPRINTF(format,...)
#endif
struct user {
char *name;
unsigned int uid;
unsigned int gid;
char home[64];
char shell[64];
};
enum sort_type {
SORT_NAME,
SORT_UID,
SORT_NONE
};
struct user *user_core(struct user* users, int *num);
struct user *expand_malloc(struct user* users);
void print_users(struct user users[], int count);
void free_users(struct user users[], int count);
int compare(const void *a, const void *b);
static int _size = 2;
static enum sort_type sort_type;
struct user *user_core(struct user *users, int *num){
struct user *u = users;
char buf[BUF_SIZE];
FILE *fp = fopen("/etc/passwd","r");
if(fp == NULL) goto error;
while(fgets(buf, sizeof(buf), fp) != NULL){
memset(u, 0, sizeof(*u));
int n;
n = sscanf(buf, "%m[^:]:%*[^:]:%u:%u:%*[^:]:%63[^:]:%63s",&(u->name),&(u->uid),&(u->gid),u->home,u->shell) == 5
| sscanf(buf, "%m[^:]:%*[^:]:%u:%u::%63[^:]:%63s",&(u->name),&(u->uid),&(u->gid),u->home,u->shell) == 5 ;
if(n != 0){
if((++u - users) >= _size){
users = expand_malloc(users);
u = users + (_size / 2);
}
continue;
}else{
*num = u - users;
DPRINTF("Parse Error: %d\n",n);
goto error;
}
}
*num = u - users;
DPRINTF("%d users be parsed\n", *num);
fclose(fp);
return users;
error:
if(u && u->name) free(u->name);
if(u) free(u);
if(fp) fclose(fp);
exit(EXIT_FAILURE);
}
struct user *expand_malloc(struct user* users){
struct user *new = users;
_size = _size * 2;
new = (struct user *)realloc(users, sizeof(struct user) * _size);
if(new == NULL) exit(EXIT_FAILURE);
return new;
}
void print_users(struct user users[], int count){
struct user *u = users;
DPRINTF("Name\tuid\tgid\thome\tshell\n");
for(int i = 0; i < count; i++, u++){
DPRINTF("%s\t%u\t%u\t%s\t%s\n",u->name,u->uid,u->gid,u->home,u->shell);
}
}
void free_users(struct user users[], int count){
for(int i = 0; i < count; i++){
free(users[i].name);
}
free(users);
}
int compare(const void *a, const void *b){
switch(sort_type)
{
case SORT_NAME:
return strcmp(((struct user *)a)->name, ((struct user *)b)->name);
break;
case SORT_UID:
return ((struct user *)a)->uid - ((struct user *)b)->uid;
break;
default:
DPRINTF("sort_type Invalid.\n");
exit(EXIT_FAILURE);
}
return -1;
}
int main(int argc, char *argv[]){
struct user *users = malloc(sizeof(struct user) *_size);
sort_type = SORT_NONE;
int opt;
int count = 0;
users = user_core(users, &count);
while((opt = getopt(argc, argv, "nd")) != -1){
switch(opt){
case 'n':
sort_type = SORT_NAME;
break;
case 'd':
sort_type = SORT_UID;
break;
default:
(void)fprintf(stderr, "Usage: %s -[nd]\n",argv[0]);
exit(EXIT_FAILURE);
}
}
if(optind > argc){
fprintf(stderr, "Expected argument after options\n");
exit(EXIT_FAILURE);
}
DPRINTF("\033[0;33m+====================================================+\033[5m\n");
print_users(users, count);
qsort(users, count, sizeof(struct user), compare);
DPRINTF("\033[0;32m+=====================Qsort=========================+\033[5m\n");
print_users(users, count);
free_users(users, count);
return 0;
}
输出结果:
每天进步一点点,上传一张苏州落日下的大裤衩照片,很不错哦
关注知乎:
C语言 每日一题(2)