Linux C 编程
在此推荐一个非常好的学习资料
统计文件单词数量
统计一个文件中的单词数量,可以将文件中的所有字符分为字母和分隔符。分隔符不属于单词内部,故定义一个状态机,来记录当前读取字符的状态。用 OUT
表示此字符不属于一个单词,用 IN
表示此字符属于一个单词。首先状态机初始化为 OUT
,打开文件并按字符读取文件中的所有内容,判断读到的字符是否为分隔符,如果是则修改状态机为 OUT
,表示不在单词内部;如果读到的字符不是分割符,并且此时的状态机为 OUT
,表示此时属于单词内部,则将状态机的状态改为 IN
。这种统计文件单词数量的本质是统计从 OUT
变为 IN
的次数,只有状态机发生变化了,表示遇到单词的首字符。因此在每次从 OUT
转变为 IN
的时候,可以累计单词的数量。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define OUT 0
#define IN 1
#define INIT OUT
int64_t count_word(const char *pathname);
int split(int ch);
int main(int argc, char *argv[]) {
if (2 != argc) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
exit(EXIT_FAILURE);
}
int64_t res = count_word(argv[1]);
if (-1 == res)
puts("open file error");
else
printf("%s has %ld words\n", argv[1], res);
return 0;
}
int64_t count_word(const char *pathname) {
FILE *fp = fopen(pathname, "r");
if (NULL == fp)
return -1;
int ch = 0;
int state = INIT;
int words = 0;
while (EOF != (ch = fgetc(fp))) {
if (split(ch)) {
state = OUT;
} else if (OUT == state) {
state = IN;
words++;
}
}
return words;
}
int split(int ch) {
if (' ' == ch || '\t' == ch || '\n' == ch || ',' == ch || '.' == ch || '{' == ch || '}' == ch || '+' == ch ||
';' == ch || ':' == ch || '!' == ch || '?' == ch || '(' == ch || ')' == ch || '\'' == ch || '\"' == ch ||
'-' == ch)
return 1;
else
return 0;
}
课后习题:统计每个单词出现的次数。
实现通讯录
在实现一个完整的项目之前,首先分析需要哪些功能,此项目需要有以下几个功能:
- 添加一个人员
- 删除一个人员
- 查找一个人员
- 打印所有人员的信息
- 保存到文件
- 读取文件中的信息
确定完功能以后,分析人员如何存储、文件如何存储、人员包含哪些信息等问题:
- 人员如何存储:数组和链表都可以使用,但在不知道具体的数量时,优先使用链表。并且,插入和删除操作,链表优于数组
- 文件如何存储:存储的方式是自定义的,此处将其存储为
name: xxx, phone: xxx
的形式 - 人员信息包含哪些:这个根据自己的想法来,此处只包含人员的姓名和手机号
开始编写代码前,一定要对项目进行分层分析,通过分层实现的程序可读性好,而且易于维护。此项目的基本结构如下所示,从底层一步一步向上封装,减少功能模块之间的依赖性。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMESIZE 64
#define PHONESIZE 16
#define PATHNAMESIZE 1024
#define BUFFERSIZE 1024
#define INFO printf
// 定义人员信息的数据结构
struct person {
char name[NAMESIZE];
char phone_num[PHONESIZE];
struct person *next;
struct person *prev;
};
// 定义通讯录数据结构
struct contacts {
struct person *people;
int counts;
};
// 操作选项,减少纯数字带来的魔法
enum {
INSERT_ENTRY = 1,
DELETE_ENTRY,
SEARCH_ENTRY,
TRAVERSAL_ENTRY,
SAVE_ENTRY,
LOAD_ENTRY,
};
// 链表操作
#define LIST_INSERT(list, item) do { \
item->next = (list); \
item->prev = NULL; \
if (NULL != (list)) \
(list)->prev = item; \
(list) = item; \
} while (0)
#define LIST_DELETE(list, item) do { \
if (NULL != item->next) \
item->next->prev = item->prev; \
if (NULL != item->prev) \
item->prev->next = item->next; \
if (list == item) \
(list) = item->next; \
item->prev = NULL; \
item->next = NULL; \
} while (0)
// 支持层
int list_insert(struct person **people, struct person *ps);
int list_delete(struct person **people, struct person *ps);
struct person *list_search(struct person *people, const char *name);
void list_traversal(struct person *people);
int load_file(struct person **people, int *counts, const char *pathname);
int save_file(struct person *people, const char *pathname);
// 接口层
int insert_entry(struct contacts *cta);
int delete_entry(struct contacts *cta);
int search_entry(struct contacts *cta);
int traversal_entry(const struct contacts *cta);
int load_entry(struct contacts *cta);
int save_entry(const struct contacts *cta);
int parser(char *line, size_t len, char *name, char *phone);
void free_list(struct person *people);
// 业务层
void menu_info();
int main(int argc, char *argv[]) {
// 创建一个通讯录
struct contacts *cta = malloc(sizeof(struct contacts));
if (NULL == cta) {
INFO("malloc error\n");
exit(EXIT_FAILURE);
}
memset(cta, 0, sizeof(struct contacts));
while (1) {
menu_info();
int choice = 0;
scanf("%d", &choice);
while (getchar() != '\n')
continue;
int res = 0;
switch(choice) {
case INSERT_ENTRY:
res = insert_entry(cta);
if (0 > res)
goto exit;
break;
case DELETE_ENTRY:
res = delete_entry(cta);
if (-1 == res)
goto exit;
break;
case SEARCH_ENTRY:
res = search_entry(cta);
if (0 > res)
goto exit;
break;
case TRAVERSAL_ENTRY:
res = traversal_entry(cta);
if (-1 == res)
goto exit;
break;
case LOAD_ENTRY:
res = load_entry(cta);
if (0 > res)
goto exit;
break;
case SAVE_ENTRY:
res = save_entry(cta);
if (0 > res)
goto exit;
break;
default:
goto exit;
}
}
exit:
// 释放所有内存
free_list(cta->people);
free(cta);
return 0;
}
void menu_info() {
puts("******************************");
puts("* 1. insert\t2. delete *");
puts("* 3. search\t4. traversal *");
puts("* 5. save\t6. load *");
puts("* input other number to quit *");
puts("******************************");
}
void free_list(struct person *people) {
struct person *temp;
while (people != NULL) {
temp = people;
people = people->next;
free(temp);
}
}
int load_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
INFO("Please input file name: ");
char pathname[PATHNAMESIZE] = {0};
scanf("%s", pathname);
while(getchar() != '\n')
continue;
return load_file(&cta->people, &cta->counts, pathname);
}
int save_entry(const struct contacts *cta) {
if (NULL == cta)
return -1;
INFO("Please input file name: ");
char pathname[PATHNAMESIZE] = {0};
scanf("%s", pathname);
while(getchar() != '\n')
continue;
return save_file(cta->people, pathname);
}
int insert_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
struct person *ps = malloc(sizeof(struct person));
if (NULL == ps)
return -2;
memset(ps, 0, sizeof(struct person));
INFO("Please input name: ");
scanf("%s", ps->name);
while (getchar() != '\n')
continue;
INFO("Please input phone: ");
scanf("%s", ps->phone_num);
while (getchar() != '\n')
continue;
if (-1 == list_insert(&cta->people, ps)) {
free(ps);
return -3;
}
cta->counts++;
INFO("Insert success!\n");
return 0;
}
int delete_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
INFO("Please input name: ");
char name[NAMESIZE] = {0};
scanf("%s", name);
while (getchar() != '\n')
continue;
struct person *item = list_search(cta->people, name);
if (NULL != item) {
if (-1 == list_delete(&cta->people, item))
return -1;
free(item);
cta->counts--;
INFO("delete success\n");
} else {
INFO("no this member\n");
}
return 0;
}
int search_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
INFO("Please input name: ");
char name[NAMESIZE] = {0};
scanf("%s", name);
while (getchar() != '\n')
continue;
struct person *item = list_search(cta->people, name);
if (NULL != item)
INFO("name: %s, pthone: %s\n", item->name, item->phone_num);
else
INFO("no this member\n");
}
int traversal_entry(const struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
list_traversal(cta->people);
}
int parser(char *line, size_t len, char *name, char *phone) {
if (len <= 0)
return -1;
char *token = strtok(line, ",");
if (token != NULL) {
sscanf(token, "name: %s", name); // 解析 name
}
token = strtok(NULL, ",");
if (token != NULL) {
sscanf(token, " phone: %s", phone); // 解析 phone
}
if (strlen(name) == 0 || strlen(phone) == 0) {
INFO("Invalid entry, parsing failed!\n");
return -1;
}
INFO("Parsed name: %s, phone: %s\n", name, phone);
return 0;
}
int load_file(struct person **people, int *counts, const char *pathname) {
FILE *fp = fopen(pathname, "r");
if (NULL == fp)
return -1;
while (!feof(fp)) {
char buffer[BUFFERSIZE] = {0};
fgets(buffer, BUFFERSIZE, fp); // 确保获取一整行的内容
char name[NAMESIZE] = {0};
char pnum[PHONESIZE] = {0};
if (0 > parser(buffer, strlen(buffer), name, pnum)) // 解析获取姓名和号码
continue;
struct person *item = malloc(sizeof(struct person));
if (NULL == item) {
save_file(*people, pathname);
fclose(fp);
return -2;
}
memset(item, 0, sizeof(struct person));
memcpy(item->name, name, NAMESIZE-1);
memcpy(item->phone_num, pnum, PHONESIZE-1);
list_insert(people, item); // 将文件中读取的信息放入到现在的链表中
(*counts)++;
}
fclose(fp);
return 0;
}
int save_file(struct person *people, const char *pathname) {
if (NULL == people)
return -1;
FILE *fp = fopen(pathname, "w");
if (NULL == fp)
return -2;
struct person *item = NULL;
for (item = people; item != NULL; item = item->next) {
fprintf(fp, "name: %s, phone: %s\n", item->name, item->phone_num);
fflush(fp);
}
fclose(fp);
INFO("save success!\n");
return 0;
}
int list_insert(struct person **people, struct person *ps) {
if (NULL == people || NULL == ps)
return -1;
LIST_INSERT(*people, ps);
return 0;
}
int list_delete(struct person **people, struct person *ps) {
if (NULL == people || NULL == ps)
return -1;
LIST_DELETE(*people, ps);
return 0;
}
struct person *list_search(struct person *people, const char *name) {
struct person *item = NULL;
for (item = people; item != NULL; item = item->next) {
if (!strcmp(item->name, name))
break;
}
return item;
}
void list_traversal(struct person *people) {
struct person *item = NULL;
for (item = people; item != NULL; item = item->next) {
INFO("name: %s, pthone: %s\n", item->name, item->phone_num);
}
}
课后习题:按照姓名首字母存储通讯录,使用数组将按首字母分为 26 组,每个人员根据首字母存放到对应的分组中。支持层的代码无需修改,在接口层实现对数组的访问。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMESIZE 64
#define PHONESIZE 16
#define PATHNAMESIZE 1024
#define BUFFERSIZE 1024
#define INFO printf
typedef struct person *(arr_ptr)[26];
// 定义人员信息的数据结构
struct person {
char name[NAMESIZE];
char phone_num[PHONESIZE];
struct person *next;
struct person *prev;
};
// 定义通讯录数据结构
struct contacts {
struct person *people[26]; // 使用数组表示首字母的情况,每个数组中存储对应的一个链表
int counts;
};
// 操作选项,减少纯数字带来的魔法
enum {
INSERT_ENTRY = 1,
DELETE_ENTRY,
SEARCH_ENTRY,
TRAVERSAL_ENTRY,
SAVE_ENTRY,
LOAD_ENTRY,
};
// 链表操作
#define LIST_INSERT(list, item) \
do { \
item->next = (list); \
item->prev = NULL; \
if (NULL != (list)) \
(list)->prev = item; \
(list) = item; \
} while (0)
#define LIST_DELETE(list, item) \
do { \
if (NULL != item->next) \
item->next->prev = item->prev; \
if (NULL != item->prev) \
item->prev->next = item->next; \
if ((list) == item) \
(list) = item->next; \
item->prev = NULL; \
item->next = NULL; \
} while (0)
// 支持层
int list_insert(struct person **people, struct person *ps);
int list_delete(struct person **people, struct person *ps);
struct person *list_search(struct person *people, const char *name);
void list_traversal(struct person *people);
int load_file(arr_ptr *people, int *counts, const char *pathname);
int save_file(struct person *people, const char *pathname, int flag);
// 接口层
int insert_entry(struct contacts *cta);
int delete_entry(struct contacts *cta);
int search_entry(struct contacts *cta);
int traversal_entry(const struct contacts *cta);
int load_entry(struct contacts *cta);
int save_entry(const struct contacts *cta);
int parser(char *line, size_t len, char *name, char *phone);
int get_index(int ch);
void free_list(struct person *people);
// 业务层
void menu_info();
int main(int argc, char *argv[]) {
// 创建一个通讯录
struct contacts *cta = malloc(sizeof(struct contacts));
if (NULL == cta) {
INFO("malloc error\n");
exit(EXIT_FAILURE);
}
memset(cta, 0, sizeof(struct contacts));
while (1) {
menu_info();
int choice = 0;
scanf("%d", &choice);
while (getchar() != '\n') continue;
int res = 0;
switch (choice) {
case INSERT_ENTRY:
res = insert_entry(cta);
if (0 > res)
goto exit;
break;
case DELETE_ENTRY:
res = delete_entry(cta);
if (-1 == res)
goto exit;
break;
case SEARCH_ENTRY:
res = search_entry(cta);
if (0 > res)
goto exit;
break;
case TRAVERSAL_ENTRY:
res = traversal_entry(cta);
if (-1 == res)
goto exit;
break;
case LOAD_ENTRY:
res = load_entry(cta);
if (0 > res)
goto exit;
break;
case SAVE_ENTRY:
res = save_entry(cta);
if (0 > res)
goto exit;
break;
default:
goto exit;
}
}
exit:
for (int i = 0; i < 26; ++i) // 释放所有内存
free_list(cta->people[i]);
free(cta);
return 0;
}
void menu_info() {
puts("******************************");
puts("* 1. insert\t2. delete *");
puts("* 3. search\t4. traversal *");
puts("* 5. save\t6. load *");
puts("* input other number to quit *");
puts("******************************");
}
void free_list(struct person *people) {
struct person *temp;
while (people != NULL) {
temp = people;
people = people->next;
free(temp);
}
}
int get_index(int ch) {
return tolower(ch) - 'a';
}
int load_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
INFO("Please input file name: ");
char pathname[PATHNAMESIZE] = {0};
scanf("%s", pathname);
while (getchar() != '\n') continue;
load_file(&cta->people, &cta->counts, pathname);
return 0;
}
int save_entry(const struct contacts *cta) {
if (NULL == cta)
return -1;
INFO("Please input file name: ");
char pathname[PATHNAMESIZE] = {0};
scanf("%s", pathname);
while (getchar() != '\n') continue;
for (int i = 0; i < 26; ++i)
save_file(cta->people[i], pathname, i + 1);
return 0;
}
int insert_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
struct person *ps = malloc(sizeof(struct person));
if (NULL == ps)
return -2;
memset(ps, 0, sizeof(struct person));
INFO("Please input name: ");
scanf("%s", ps->name);
int index = get_index(ps->name[0]);
while (getchar() != '\n') continue;
INFO("Please input phone: ");
scanf("%s", ps->phone_num);
while (getchar() != '\n') continue;
if (-1 == list_insert(&cta->people[index], ps)) {
free(ps);
return -3;
}
cta->counts++;
INFO("Insert success!\n");
return 0;
}
int delete_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
INFO("Please input name: ");
char name[NAMESIZE] = {0};
scanf("%s", name);
int index = get_index(name[0]);
while (getchar() != '\n') continue;
struct person *item = list_search(cta->people[index], name);
if (NULL != item) {
if (-1 == list_delete(&cta->people[index], item))
return -1;
free(item);
cta->counts--;
INFO("delete success\n");
} else {
INFO("no this member\n");
}
return 0;
}
int search_entry(struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
INFO("Please input name: ");
char name[NAMESIZE] = {0};
scanf("%s", name);
int index = get_index(name[0]);
while (getchar() != '\n') continue;
struct person *item = list_search(cta->people[index], name);
if (NULL != item)
INFO("name: %s, pthone: %s\n", item->name, item->phone_num);
else
INFO("no this member\n");
}
int traversal_entry(const struct contacts *cta) {
if (NULL == cta)
return -1;
if (0 == cta->counts) {
INFO("contact is empty!\n");
return -2;
}
for (int i = 0; i < 26; ++i) list_traversal(cta->people[i]);
}
int parser(char *line, size_t len, char *name, char *phone) {
if (len <= 0)
return -1;
char *token = strtok(line, ",");
if (token != NULL) {
sscanf(token, "name: %s", name); // 解析 name
}
token = strtok(NULL, ",");
if (token != NULL) {
sscanf(token, " phone: %s", phone); // 解析 phone
}
if (strlen(name) == 0 || strlen(phone) == 0) {
INFO("Invalid entry, parsing failed!\n");
return -1;
}
INFO("Parsed name: %s, phone: %s\n", name, phone);
return 0;
}
int load_file(arr_ptr *people, int *counts, const char *pathname) {
FILE *fp = fopen(pathname, "r");
if (NULL == fp)
return -1;
while (!feof(fp)) {
char buffer[BUFFERSIZE] = {0};
fgets(buffer, BUFFERSIZE, fp); // 确保获取一整行的内容
char name[NAMESIZE] = {0};
char pnum[PHONESIZE] = {0};
if (0 > parser(buffer, strlen(buffer), name, pnum)) // 解析获取姓名和号码
continue;
int index = get_index(name[0]);
struct person *item = malloc(sizeof(struct person));
if (NULL == item) {
fclose(fp);
return -2;
}
memset(item, 0, sizeof(struct person));
memcpy(item->name, name, NAMESIZE - 1);
memcpy(item->phone_num, pnum, PHONESIZE - 1);
list_insert(&(*people)[index], item); // 将文件中读取的信息放入到现在的链表中
(*counts)++;
}
fclose(fp);
return 0;
}
int save_file(struct person *people, const char *pathname, int flag) {
if (NULL == people)
return -1;
FILE *fp = NULL;
// flag 为 1 时覆盖写入,否则追加写入
if (flag == 1)
fp = fopen(pathname, "w");
else
fp = fopen(pathname, "a+");
if (NULL == fp)
return -2;
struct person *item = NULL;
for (item = people; item != NULL; item = item->next) {
fprintf(fp, "name: %s, phone: %s\n", item->name, item->phone_num);
fflush(fp);
}
fclose(fp);
INFO("save success!\n");
return 0;
}
int list_insert(struct person **people, struct person *ps) {
if (NULL == people || NULL == ps)
return -1;
LIST_INSERT(*people, ps);
return 0;
}
int list_delete(struct person **people, struct person *ps) {
if (NULL == people || NULL == ps)
return -1;
LIST_DELETE(*people, ps);
return 0;
}
struct person *list_search(struct person *people, const char *name) {
struct person *item = NULL;
for (item = people; item != NULL; item = item->next) {
if (!strcmp(item->name, name))
break;
}
return item;
}
void list_traversal(struct person *people) {
struct person *item = NULL;
for (item = people; item != NULL; item = item->next)
INFO("name: %s, pthone: %s\n", item->name, item->phone_num);
}