Linux C 编程

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

课后习题:统计每个单词出现的次数。

实现通讯录

在实现一个完整的项目之前,首先分析需要哪些功能,此项目需要有以下几个功能:

  1. 添加一个人员
  2. 删除一个人员
  3. 查找一个人员
  4. 打印所有人员的信息
  5. 保存到文件
  6. 读取文件中的信息

确定完功能以后,分析人员如何存储、文件如何存储、人员包含哪些信息等问题:

  • 人员如何存储:数组和链表都可以使用,但在不知道具体的数量时,优先使用链表。并且,插入和删除操作,链表优于数组
  • 文件如何存储:存储的方式是自定义的,此处将其存储为 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xjjeffery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值