目录
- 前言
- 部分功能、开发环境与项目结构
- 主函数设计
- statistics 头文件以及源文件
- statistics.h
- statistics.c
- encryption 头文件以及源文件
- encryption.h
- encryption.c
前言
耗时一天一夜写了一个简易的《英文统计和加密系统》,实际上就是对字符数组的基本操作的各种使用,其中也牵扯到简单的读写文件,把结构体存入文本文件等知识。总体来说,只要在编写前搭建好一个思维框架,逐个实现功能就轻松的多了。
部分功能、开发环境与项目结构
部分功能展示:
开发环境:
我使用的开发环境是 Visual Stduio 2022(2019版本应该更好)。
项目结构:
包含两个头文件和对应的源文件, main.c,以及三个文本文件:
主函数设计
// 英文统计和加密系统
// statistics 统计
// encryption 加密(结构体相关功能也在此处)
#include"statistics.h"
#include"encryption.h"
int main() {
char buf[BUFSIZE];
memset(buf, '0', sizeof(buf)); // 给字符数组初始化为 '0'
showInfo(buf);
printf("当前字母数:%d\n",countCharacter(buf));
printf("当前单词数:%d\n",countWord(buf));
printf("第12个字母为:");
show_i(buf, 12);
printf("与字母 'b'相比,小于,等于,大于的字母个数分别为:");
classify_c(buf, 'b');
int x = count_appear(buf, 'y');
printf("字母y(不区分大小写)出现的次数为:%d\n", x);
printf("所有y出现的下标为:");
int* A = (int*)malloc(sizeof(int) * x +1);
int *n = location_index(buf, A, 'y');
for (int i = 0; i < x; i++) {
printf("%3d", n[i]);
}
printf("\n");
encryption(buf, 3);
/*puts(buf);*/
printf("将26个英文字母打包成一个结构体,附加对应次数\n");
alphabet chars;
Initialize(&chars);
printf("计算字母出现次数并存入文本文件中:\n");
count_letters(buf, &chars);
encrypt en = maxTimes(chars);
encrypt minEn = minTimes(chars);
printf("出现最多的字母为:%c,对应次数为:%d\n", en.data, en.num);
printf("出现最少的字母为:%c,对应次数为:%d\n", minEn.data, minEn.num);
printf("字母平均出现次数:%.2f\n", averageNum(chars));
int k = 8;
char* list = list_k(chars,k);
printf("前%d名字母序列为:", k);
for (int i = 0; i < k; i++) {
printf("%2c", list[i]);
}
select_sort(chars);
//setText(&chars); // 需要更新结构体文本文件时使用
}
主函数完全可以使用 循环+switch-case
来做一个菜单,重复使用功能。
实现效果已经在上文中展示,后序不再展示。
statistics 头文件以及源文件
statistics.h
#pragma once // 避免重复包含头文件
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 512
// 获取原文长度
int get_length(char* buf);
// 获取文本的原文内容
void getText(char *buf);
// 得到原文内容
void showInfo(char *buf);
// 统计原文字母个数
int countCharacter(char* buf);
// 统计原文单词个数
int countWord(char* buf);
// 显示原文第i个单词
void show_i(char* buf,int i);
// 根据字符c的大小,分为三部分
void classify_c(char* buf, char c);
// 定位值为c的下标
int* location_index(char* buf, int* index,char c);
// 字符c的出现次数
int count_appear(char* buf, char c);
statistics.c
#include"statistics.h"
// 获取原文有效长度
int get_length(char* buf) {
int len = 0;
for (int i = 0; i < BUFSIZE && buf[i] != '0'; i++) {
len++;
}
return len;
}
//用子函数实现从文本文件读取原文的操作
void getText(char *buf) {
FILE* file;
file = fopen("myfile.txt", "r");
if (file == NULL) {
printf("打开文件失败!");
exit(-1);
}
char arr[BUFSIZE];
memset(arr, '0', sizeof(arr));
fgets(arr, sizeof(arr), file);
fclose(file);
strcpy(buf, arr);
}
//1.定义一字符数组word,用户输入原文或从一文本文件读入(调用子函数)原文(一段英文),
// 输出显示该原文,以及其总长度。
void showInfo(char* buf) {
printf("1.输入原文:\n");
printf("2.读取原文\n");
int choice = 0;
scanf("%d", &choice);
rewind(stdin); // 清空缓冲区
if (choice == 1) {
char arr[BUFSIZE];
memset(arr, '0', sizeof(arr));
fgets(arr, sizeof(arr), stdin);
strcpy(buf, arr);
}
else if (choice == 2) {
getText(buf);
}
else {
printf("请正确选择获取原文方式!"); exit(-1);
}
int sum = get_length(buf);
if (choice==2) printf("总长度为:%d\n", --sum); // 去掉自带的 .
else printf("总长度为:%d\n", sum-2); // fgets会多读一个字符
printf("原文:%s\n", buf);
}
//3.用子函数实现统计原文中的字母个数。
int countCharacter(char* buf) {
int i;
int count = 0;
for (i = 0; i < BUFSIZE&&buf[i]!= '0'; i++) {
if (buf[i] >= 'a' && buf[i] <= 'z' || buf[i] >= 'A' && buf[i] <= 'Z') {
count++;
}
}
return count;
}
//4.用子函数实现统计原文中的单词个数。
int countWord(char* buf) {
int i;
int count = 0;
for (i = 1; i < BUFSIZE&&buf[i]!='0'; i++) {
if (buf[i] == ' ' &&(buf[i - 1] >= 'a' && buf[i - 1] <= 'z' || buf[i - 1] >= 'A' && buf[i - 1] <= 'Z'))
count++;
}
if (buf[i - 1] >= 'a' && buf[i - 1] <= 'z' || buf[i - 1] >= 'A' && buf[i - 1] <= 'Z')
count++;
return count;
}
//5.定义一子函数,输入任一下标值i,检查i的输入合法性,
// 合法输出显示word的第i个字母,否则输出“输入i不合法”。
void show_i(char* buf,int i) {
int len = countCharacter(buf);
if (i<1 || i>len) {
printf("输入i不合法\n");
exit(-1);
}
int index = 0; // 记录当前字母位序
for (int j = 0; j < BUFSIZE && buf[j] != '0'; j++) {
if (buf[j] >= 'a' && buf[j] <= 'z' || buf[j] >= 'A' && buf[j] <= 'Z') {
++index;
if (index == i) {
printf("%c\n", buf[j]);
return;
}
}
}
}
//6.定义一子函数,输入一字母c, 分别输出word中大于字母c的个数和小于字母c的个数,
// 以及等于字母c的个数。
void classify_c(char* buf, char c) {
char arr[BUFSIZE];
memset(arr, '0', sizeof(arr));
int index = 0; // 记录当前字母位序
for (int j = 0; j < BUFSIZE && buf[j] != '0'; j++) {
if (buf[j] >= 'a' && buf[j] <= 'z' || buf[j] >= 'A' && buf[j] <= 'Z') {
arr[index++] = buf[j];
}
}
// 冒泡排序(升序)
for (int i = 0; i < index; i++) {
char flag = 'f';// 标志位
for (int j = index-1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
flag = 't';
char ch = arr[j];
arr[j]=arr[j-1];
arr[j - 1] = ch;
}
}
if (flag == 'f') break;
}
int l, h, e,i;
for (l = 0, h = 0, e = 0, i = 0; i < index; i++) {
if (arr[i] < c) l++;
else if (arr[i] == c) e++;
else break;
}
h = index - l - e;
printf("little:%d equal:%d high:%d\n", l, e, h);
}
//7.定义一子函数,实现字符查找功能:判定该字母是否存在,
// 是则返回每一个出现位置的下标(或者首次出现的下标位置),否则返回 - 1。
int* location_index(char* buf, int *index,char c) {
int i;
int in = 0;
char flag = 'f';// 如果最终还是f,说明找不到该字母
for (i = 0; i < BUFSIZE && buf[i] != '0'; i++) {
if (buf[i] == c||c == buf[i]+32||c==buf[i]-32) index[in++] = i;
flag = 't';
}
if (flag == 'f') return -1;
return index;
}
//8.定义一子函数,实现输入一字母,统计原文中该字母的出现次数(字母不区分大小写, 实现合并计数)
// 并返回。函数中需要有对输入的字母是否输入异常判定和捕获操作,并在主程序中进行输入错误提示。
int count_appear(char* buf, char c) {
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
printf("当前字符为%c,请输入正确字母\n", c);
exit(-1);
}
int i;
int count = 0;
for (i = 0; i < BUFSIZE && buf[i] != '0'; i++) {
if (c == buf[i] || c == buf[i] + 32 || c == buf - 32) count++;
}
return count;
}
encryption 头文件以及源文件
encryption.h
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// 字母以及出现次数的结构体
typedef struct {
char data; // 显示字母
int num; // 出现次数
}encrypt;
typedef struct {
encrypt letters[26];
}alphabet;
// 初始化字母表
void Initialize(alphabet *chars);
// 计算各个字母的出现次数
void count_letters(char* buf, alphabet* chars);
// 存入结构体文本文件
void setText(alphabet *chars);
// 返回出现次数最多的字母
encrypt maxTimes(alphabet chars);
// 返回出现次数最少的字母
encrypt minTimes(alphabet chars);
// 计算字母出现的平均次数
float averageNum(alphabet chars);
// 出现前k名的字母序列
char* list_k(alphabet chars, int k);
// 选择升序或者降序排序
void select_sort(alphabet chars);
// 加密算法,根据n的位置循环后移
void encryption(char* buf, int n);
// 存入加密的文本文件
void setEncryptText(char* buff);
encryption.c
#include"encryption.h"
#include"statistics.h"
//9.将26个英文字母和其对应的出现次数打包定义为一个结构体类型来存储。
// 初始化结构体数组
void Initialize(alphabet* chars) {
int k = 0;
char c = 'a';
for (int i = 0; i < 26; i++) {
chars->letters[i].num = k;
chars->letters[i].data = c + i;
}
}
//10.设计实现一新的子函数,针对读入的原文,统计出26个英文字母在单词中出现的次数,
//并统一存储到9定义的结构体数组中,然后完成11到16的操作。
void count_letters(char* buf, alphabet* chars) {
printf("1.输入原文:\n");
printf("2.读取原文\n");
int choice = 0;
int sum = 0;
scanf("%d", &choice);
rewind(stdin); // 清空缓冲区
if (choice == 1) {
char arr[BUFSIZE];
memset(arr, '0', sizeof(arr));
fgets(arr, sizeof(arr), stdin);
strcpy(buf, arr);
}
else if (choice == 2) {
getText(buf);
}
else {
printf("请正确选择获取原文方式!"); exit(-1);
}
char arr[BUFSIZE]; // 最终转化为全部字母
memset(arr, '0', sizeof(arr));
int index = 0; // 记录当前字母位序
for (int j = 0; j < BUFSIZE && buf[j] != '0'; j++) {
if (buf[j] >= 'a' && buf[j] <= 'z' || buf[j] >= 'A' && buf[j] <= 'Z') {
arr[index++] = buf[j];
}
}
for (int i = 0; i < index; i++) {
for (int j = 0; j < 26; j++) {
if (arr[i] == chars->letters[j].data || arr[i] + 32 == chars->letters[j].data)
chars->letters[j].num++;
}
}
}
//11.将结构体数组结果存储到一文本文件。
void setText(alphabet* chars) {
FILE* fp;
fopen_s(&fp, "struct.txt", "wt+"); //打开文件
for (int i = 0; i < 26; i++) //将N条信息存储进去
{
fprintf(fp, "%d %d\n", chars->letters[i].data, chars->letters[i].num);
}
fclose(fp); //关闭文件
//encrypt buff[26];
//FILE* fpp;
//fopen_s(&fpp, "struct.txt", "rb");
//fread(buff, sizeof(encrypt), 26, fpp); // 将N条消息全部从文件中读取出来
//fclose(fpp);
}
//12.设计一子函数:返回出现次数最多的字母和对应次数。
encrypt maxTimes(alphabet chars) {
int max = chars.letters[0].num;
int i, index;
for (i = 1, index = 0; i < 26; i++) {
if (max < chars.letters[i].num) {
max = chars.letters[i].num;
index = i;
}
}
return chars.letters[index];
}
//13.设计一子函数:返回出现次数最少的字母和对应次数。
encrypt minTimes(alphabet chars) {
int min = chars.letters[0].num;
int i, index;
for (i = 1, index = 0; i < 26; i++) {
if (min > chars.letters[i].num) {
min = chars.letters[i].num;
index = i;
}
}
return chars.letters[index];
}
//14.设计一子函数:返回26个字母的平均出现次数。
float averageNum(alphabet chars) {
float avg = 0.0;
for (int i = 0; i < 26; i++) {
avg += chars.letters[i].num;
}
avg /= 26;
return avg;
}
//15. 设计一子函数:输入数字k,返回出现次数前k名的字母序列
char* list_k(alphabet chars, int k) {
if (k < 1 || k>26) exit(-1);
char* list = (char*)malloc(sizeof(char) * k);
// 冒泡排序
for (int i = 0; i < 26; i++) {
char flag = 'f';
for (int j = 25; j > i; j--) {
if (chars.letters[j].num > chars.letters[j - 1].num) {
flag = 't';
encrypt temp = chars.letters[j];
chars.letters[j] = chars.letters[j - 1];
chars.letters[j - 1] = temp;
}
}
if (flag == 'f') break;
}
for (int i = 0; i < k; i++) {
list[i] = chars.letters[i].data;
}
return list;
}
//16.定义一排序子函数,实现对结构体数组结果按照出现次数进行升序或降序排列
//(由用户在运行时选择排序方式), 输出排序结果以及对应的字母。
void select_sort(alphabet chars) {
printf("\n1.升序排列\n2.降序排列\n");
int choice = 0;
scanf("%d", &choice);
if (choice == 2) {
for (int i = 0; i < 26; i++) {
char flag = 'f';
for (int j = 25; j > i; j--) {
if (chars.letters[j].num > chars.letters[j - 1].num) {
flag = 't';
encrypt temp = chars.letters[j];
chars.letters[j] = chars.letters[j - 1];
chars.letters[j - 1] = temp;
}
}
if (flag == 'f') break;
}
}
else if (choice == 1)
{
for (int i = 0; i < 26; i++) {
char flag = 'f';
for (int j = 25; j > i; j--) {
if (chars.letters[j].num < chars.letters[j - 1].num) {
flag = 't';
encrypt temp = chars.letters[j];
chars.letters[j] = chars.letters[j - 1];
chars.letters[j - 1] = temp;
}
}
if (flag == 'f') break;
}
}
else {
printf("请正确选择排序规则!");
exit(-1);
}
printf("排序结果为:");
for (int i = 0; i < 26; i++) {
printf("%2c", chars.letters[i].data);
}
}
//17.用子函数实现将读取的原文按规则加密后存入另一字符数组中。
//电文加密 : 问题描述:为使电文保密,常按一定规律将其转换成密文后发送,
//收报人再按约定的规律将其译回原文。设定加密规则为:
//每个字母变成其后的第n(n由用户输入指定)个字母,如A变成其后第n个字母….。
//说明 : 只对原文中的英文字母加密, 其他非英文字母要原样保留。
void encryption(char* buf, int n) {
n %= 26; // 确保英文字母加密后还是英文字母
int i;
int len = get_length(buf);
char* arr = (char*)malloc(len * sizeof(char));
memset(arr, '0', sizeof(arr));// 初始化
for (i = 0; i < len; i++) {
if (buf[i] >= 'a' && buf[i] <= 'z' || buf[i] >= 'A' && buf[i] <= 'Z') {
if (buf[i] + n > 'z') {
arr[i] = buf[i] + n - 26;
}
else if (buf[i] <= 'Z' && buf[i] + n > 'Z') {
arr[i] = buf[i] + n - 'Z' + 'A'-1;
}
else {
arr[i] = buf[i] + n;
}
}
else {
arr[i] = buf[i];
}
}
printf("加密后:%s\n", arr);
setEncryptText(arr);
}
//18.用子函数实现将密文存入密文文本文件中。
void setEncryptText(char* buff) {
FILE* fp;
fp = fopen("encryption.txt", "wt+");
if (fp == NULL) {
printf("打开文件失败!");
exit(-1);
}
fputs(buff, fp);
fclose(fp);
}
myfile.txt文件中可自行输入密文(26个英文字母大小写,各种符号,数字等等);
struct.txt文件有26行,对应着原文中26个英文字母出现的次数以及ASCII码值;
encryption.txt文件是myfile.txt文件中的字母通过循环后移 n 位得到,其中的难点在于大写字母后移超过Z时的处理方法,这点大家可以在encryption.c文件中的encryption函数中获得灵感。