B站视频 C++就业班 本文都是C语言的 前面的基础课程 ,从文章-2开始对应数据结构
1.文件读写相关
1.1. 文件版 四则运算
现在文件里用函数写进4个等式 但是不写结果
在while循环中,如下处理每一行
然后用fgets读出每一行,sscanf提取变量值,进入switch算出结果,然后sprintf打印出带着结果的一个完整等式。
最会strcat拼接成一个大字符串(所有内容) w 模式清空文件并重写进去。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
//文件版四则运算
//在yunsuan.txt里写了4个算式
//用fwrite写还是事先自己写
char allres[1024]={0};
void writeOne(void) {
FILE *fp1=fopen("yunsuan.txt","w");
if(!fp1){
perror("打开失败");
return ;
}
fputs("12+3=\n",fp1);
fputs("20-5=\n",fp1);
fputs("3*4=\n",fp1);
fputs("24/6=\n",fp1);
fclose(fp1);
}
void getres(){
FILE *fp2=fopen("yunsuan.txt","r");
if(!fp2){
perror("打开失败");
return ;
}
char buf[1024];//先把整个文件读出来
//这样才方便删除所有文件原来部分
int a;
int b;
char c;
int res;
char resline[100];
while(1){
fgets(buf,1024,fp2);
if(feof(fp2)){
break;
}
printf("%s",buf);
sscanf(buf,"%d%c%d=\n",&a,&c,&b);
//printf("%d %d %c\n",a,b,c);
switch (c) {
case '+':
res=a+b;
break;
case '-':
res=a-b;
break;
case '*':
res=a*b;
break;
case '/':
res=a/b;
break;
default:
printf("没有找到运算符");
break;
}
sprintf(resline,"%d%c%d=%d\n",a,c,b,res);
printf("%s\n",resline);//test0
strcat(allres,resline);
}
printf("-----------------\n");
printf("%s",allres);
fclose(fp2);
}
void rewrite(){
//w是直接全部清空的 模式
FILE *fp3=fopen("yunsuan.txt","w");
if(!fp3){
perror("打开失败");
return ;
}
fputs(allres,fp3);
fclose(fp3);
}
int main(void) {
writeOne();
getres();
rewrite();
return EXIT_SUCCESS;
system("pause");
}
1.2.fread fwrite 大文件拷贝
自己写了一个 但是只能复制图的 黑背景??。用二进制读写就没事了
自己写的函数缺陷:每次调用先得改新文件的名字。导致必须复制一次函数整体。
例1:复制yunsuan.txt 文本文件
void mycopyb(char* pathname){
FILE *fp1=fopen(pathname,"rb+");
if(!fp1){
perror("打开失败");
return ;
}
char cont[1024]={0};
FILE *fp2=fopen("mycopy.txt","wb+");
if(!fp2){
perror("打开失败");
return ;
}
while(!feof(fp1)){
fread(cont,1,1024,fp1);
fwrite(cont,1,1024,fp2);
}
fclose(fp1);
fclose(fp2) ;
}
int main(void) {
//测试一个txt文件 注意括号里参数写原文件
//函数缺陷是新文件名必须现改 重复写函数
mycopyb("yunsuan.txt");
return EXIT_SUCCESS;
system("pause");
}
例2:复制一个jpg 文件ab.jpg 看老师的代码 修改,加上一个fread的返回值,
当返回值等于0时说明读取失败
void mycopyb(char* pathname){
FILE *fp1=fopen(pathname,"rb+");
if(!fp1){
perror("打开失败");
return ;
}
char cont[1024]={0};
int ret=0;
FILE *fp2=fopen("mycopy2.jpg","wb+");
if(!fp2){
perror("打开失败");
return ;
}
while(!feof(fp1)){
ret=fread(cont,1,1024,fp1);
if(ret==0){
break;
}
fwrite(cont,1,1024,fp2);
}
fclose(fp1);
fclose(fp2) ;
}
int main(void) {
mycopyb("ab.jpg");
return EXIT_SUCCESS;
system("pause");
}
1.3 游戏配置文件解析
假设有个游戏文件,记录了游戏角色的各种信息,txt中信息如下:
#英雄ID
heroID:zhaoyun
#英雄能力
heroskill:fight
#英雄道具
herotool:bigknife
#英雄血量
heroHP:10000
#英雄防御能力
herotank:20000
通过读文件操作过滤无效信息,将记录保存到结构体数组中。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct info{
char key[256];
char val[256];
};
//多少行有效信息
int lines(){
int ll=0;
FILE *fp=fopen("gameinfo.txt","r");
if(fp==NULL){
perror("文件没有打开");
}
char buf[256]={0};
while(fgets(buf,256,fp)!=NULL){
if(strchr(buf,':')!=NULL){
printf("%s\n",buf);
ll++;
// sscanf(buf,"%s:%s",key,val);
// printf("key是%s,val是 %s\n",key,val);
//这样吧整个一行都算成key 也又算成val 了!!
//必须用字符串截取函数
//memset(key,0,128);
// memset(val,0,128);
}
memset(buf,0,256);
}
fclose(fp);
return ll;
}
//从文件中,提取有效信息放到 数组中
void putarr(struct info **infoARR,int len){
struct info *arr=(struct info *)malloc(sizeof(struct info)*len);
int index=0;
FILE *fp2=fopen("gameinfo.txt","r");
if(fp2==NULL){
perror("文件没有打开");
}
char buf2[256]={0};
while(fgets(buf2,256,fp2)!=NULL){
if(strchr(buf2,':')!=NULL){
memset(arr[index].key,0,256);
memset(arr[index].val,0,256);
char *pos=strchr(buf2,':');
strncpy(arr[index].key,buf2,strlen(buf2)-strlen(pos));
strncpy(arr[index].val,pos+1,strlen(pos-1));
index++;
if(index==len){
break;
}
}
memset(buf2,0,128);
}
fclose(fp2);
*infoARR=arr;
}
//先遍历一下数组 看看弄好了么
void printALL(struct info *arr,int len){
for(int i=0;i<len;i++){
printf("%s is %s\n",arr[i].key,arr[i].val);
}
}
//cesh测试,根据英雄的key,打印出属性值,要求对应正确。
void getval(const char *kword,struct info *arr,int len){
int flag=0;
for(int i=0;i<len;i++){
if(strcmp (arr[i].key,kword)==0){
printf("the attribute is %s ",arr[i].val);
flag=1;
}
}
if(flag==0){
printf ("no such attribution\n");
}
}
int main()
{
int nums=lines();
printf("有效信息一共 %d 行 \n",nums);
struct info *ptrIn=NULL;
putarr(&ptrIn,nums);
printALL(ptrIn,nums);
getval("heroHP",ptrIn,nums);
//getval("heroMP",ptrIn,nums); //no such attribution
system("pause");
return 0;
}
1.4 文件的加密和解密
把所有字符进行位运算,先左移4位,再右移1位,然后变更符号位。
解密时,先左移1位 再右移5位 (??高位4位会丢失,这里没看明白)
void code(const char* fileA, const char* fileB){
FILE *fp=fopen(fileA,"r");
FILE *fp2=fopen(fileB,"w");
if(fp==NULL){
perror(" open is fail\n");
}
if(fp2==NULL){
perror(" open is fail\n");
}
char ch;
short temp;
while((ch=fgetc(fp))!=EOF){
temp=(short)ch;
temp=temp<<4;
temp=temp|0x8000;//1000 0000 0000 0000 两字节哦
//printf("%d\n",temp);
fprintf(fp2,"%hd",temp);
}
fclose(fp);
fclose(fp2);
}
void decode(const char* fileB, const char* fileC){
FILE *fp3=fopen(fileB,"r");
FILE *fp4=fopen(fileC,"w");
if(fp3==NULL){
perror(" open3 is fail\n");
}
if(fp4==NULL){
perror(" open4 is fail\n");
}
char ch;
short temp;
while(!feof(fp3)){
fscanf(fp3,"%hd",&temp);
temp=temp<<1;
temp=temp>>5;
//疑问,最开始最高位那4个数字无法恢复了。。
printf("%d\n",temp);
ch=(char)temp;
fputc(ch,fp4);
}
fclose(fp3);
fclose(fp4);
}
int main()
{
code("incode.txt","decode.txt");
decode("decode.txt","res.txt");
//打开res 和incode看是不是一模一样就行
system("pause");
return 0;
}
2.指针基础属性
2.1利用指针步长,获取结构体属性的值。
struct Person{
int a;
char b;
char buf[64];
int d;
};
int main (){
struct Person p1={100,'w',"water cup",12};
printf("%d\n",sizeof(p1)); //76
// char *pp=&p1; BAO CUO ERROR
printf("属性d的值是%d\n",*((char*)&p1+72));
2.2 offsetof( struct 结构体名,属性名) 函数
#include<stddef.h>
struct Person p1={100,'w',"water cup",12};
int aaa=offsetof(struct Person,d);
printf("%d\n",aaa); //72
3.数据结构方面基础代码
3.1 带头节点的链表
创建在堆上的 叫做动态链表。头结点没有数据,只有一个指针域。
创建initList ,遍历foreach, 释放freeList 三个函数。
外加一个插入函数,第一个参数是头结点,第二个参数int old是在数据等于old的节点后面插入,第三个参数是插入的新值。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
struct Node{
int num;
struct Node* next;
};
//初始化链表
struct Node* initList(){
struct Node* phead;
if(phead==NULL){
perror("FAIL IN MALLOC");
}
phead=(struct Node *)malloc(sizeof(struct Node));
phead->next=NULL;
struct Node* ptail=phead;
int n=0;
while(1){
int m=0;
printf("Please type in the %d th number\n",n+1);
scanf("%d",&m);
if(m==-1){
printf("List ends\n");
break;
}
else{
struct Node* ptr=(struct Node *)malloc(sizeof(struct Node));
ptr->num=m;
ptr->next=NULL;
n++;
ptail->next=ptr;
ptail=ptr;
}
}
return phead;
};
//链表的遍历
void foreach(struct Node* header){
if(header==NULL){
perror("list is NULL");
}
while(header->next!=NULL){
header=header->next;
printf("the num is %d\n",header->num);
}
}
//再链表某个值的后面插入一个新值
void listinser(struct Node* header,int old,int newval){
if(header==NULL){
perror("list is NULL");
}
struct Node* ptr=header->next;
while(ptr!=NULL){
if(ptr->num==old){
break;
}
ptr=ptr->next;
}
if(ptr==NULL){
printf("no such data in the list\n");
return;
}
struct Node* newnode=( struct Node*)malloc(sizeof(struct Node));
newnode->num=newval;
newnode->next=ptr->next;
ptr->next=newnode;
}
//摧毁整个链表
void listfree(struct Node* list){
if(list==NULL){
perror("list is NULL");
}
struct Node* pp=list;
while((list->next)!=NULL){
list=list->next;
free(pp);
pp=NULL;
pp=list;
}
free(list);
list=NULL;
}
int main(void) {
struct Node* mylist=initList();
foreach(mylist);
listinser(mylist,30,2000);
printf("插入操作完成,现在链表重新遍历\n");
foreach(mylist);
listfree(mylist);
free(mylist);
mylist=NULL;
if(mylist==NULL){
printf("right\n");
}
system("pause");
return EXIT_SUCCESS;
}
3.1.1按照位置来插入 新节点。
第二个参数pos表示在第几节点后面插入新节点,头结点当做第0个。
void listinsBpos(struct Node* header,int pos,int newval){
if(header==NULL){
perror("list is NULL");
}
int inpos=0;
struct Node* ptr=header;
while(ptr!=NULL){
if(inpos==pos){
break;
}
ptr=ptr->next;
inpos++;
}
if(ptr==NULL){
printf("no such data in the list\n");
return;
}
struct Node* newnode=( struct Node*)malloc(sizeof(struct Node));
newnode->num=newval;
newnode->next=ptr->next;
ptr->next=newnode;
}
3.1.2 链表倒置
头结点为pheader ,第一个有数据域的节点为p1 , 后面依次为p2, p3 每次让p2->next = p1 然后再移动p1 p2的位置就可以了。
void reverLink( struct Node* head ){
struct Node* p1=head;
struct Node* p2=head->next;
struct Node* p3=p2->next;
if(p3==NULL){
printf("only one NODE having data\n");
return;
}
p2->next=NULL; //第一个有数据域的节点,成为尾巴,next 为NULL
p1=p2;
p2=p3;
while(p2!=NULL){
struct Node* pnext=p2->next;
p2->next=p1;
p1=p2;
p2=pnext;
}
head->next=p1;//把头节点挪过来,让它指向原来的尾巴
}
4.模拟或者改写字符串相关API
4.1 字符串复制函数strcpy
3种方式 zhuyi注意第三种,赋值为0 退出while循环
void mycopy(char *str1,char *str2){
int num=strlen(str1);
for(int i=0;i<=num;i++){
str2[i]=str1[i];
}
}
void mycopy2(char *str1,char *str2){
int num=strlen(str1);
while(*str1!='\0'){
*str2=*str1;
str1++;
str2++;
}
}
void mycopy3(char *str1,char *str2){
while(*str2++=*str1++){
}
}
void mytest(){
char ss1[64]="gets water";
char ss2[64]={0};
// mycopy(ss1,ss2);
//mycopy2(ss1,ss2);
//mycopy3(ss1,ss2);
printf("%s\n",ss2);
}
4.2 字符串翻转函数
void myverse(char *str){
int num=strlen(str);
int a=0;
int b=num-1;
while(a<b){
char temp='0';
temp=str[a];
str[a]=str[b];
str[b]=temp;
a++;
b--;
}
}
利用指针
void rev2(char *buf){
int num=strlen(buf);
char temp='0';
char *start=buf;
char *end=buf+num-1;
while(start<end){
temp=*start;
*start=*end;
*end=temp;
start++;
end--;
}
}
4.3 判断一个字符串是否回文
视频中老师的写法
int str_huiwen(char *str){
char *start=str;
char *end=start+strlen(str)-1;
while(start<end){
if(*start!=*end){
return 0;
}
start++;
end--;
}
return 1;
}
自己写法
bool huiwen(char *str){
char *start=str;
char *end=start+strlen(str)-1;
while(*start==*end){
start++;
end--;
if(start>end){
return true;
break;
}
}
return false;
}
4.4 字符串查找strstr( )模拟
注意,C语言中strstr 函数是返回地址(就是子串在母串中的首地址 ), 还是一个可以打印的字符串,但是我这个函数返回的是子串的第一个字母是母串的第几个字符。
int mystrsub(const char *str,const char*sub){
int num=0;
while(*str!='\0'){
if(*str==*sub){
const char *temp1=str;
const char *temp2=sub;
while(*sub!='\0'){
if(*temp1!=*temp2){
break;
}
temp1++;
temp2++;
}
if(*temp2=='\0'){
printf("子串匹配成功位置是%d\n",num);
return num;
}
}
str++;
num++;
}
return -1;
}
void test2(){
const char *word="adegdnfhi";
const char* subword="dnf";
int ret=mystrsub(word,subword);
printf("%d\n",ret);
}
4.5 在母串中,子串出现的次数
这个是利用了库自带的strstr( )函数
字符串查找 找到的次数 利用已有的strstr <string.h>
int ser_time(char *sear,char*tar){
int k=strlen(tar);
int count=0;
char *r=strstr(sear,tar);
while(r!=nullptr){
printf("%s\n",r);
r+=k;
count++;
r=strstr(r,tar);//再新的 掐去了头的 字符串里寻找
}
return count;
}
4.6字符串复制
4.7 strlen字符串长度统计功能
//zhi利用传统功能实现 strlen功能
int mystrlen(char str[]){
int i=0;
while(str[i]!='\0'){
i++;
}
return i;
}
//zhi利用指针实现 strlen功能
int mystrlen2(char str[]){
char *cp=str;
while((*cp)!='\0'){
//'\0' 不是空格,是NULL字符啦,是一个字符串的结束
cp++;
}
return cp-str;
}
5.一些基础小例子
5.1 模拟电子表打印
#include <windows.h> //Sleep()函数
for(int i=0;i<24;i++){
for(int j=0;j<60;j++){
for(int k=0;k<60;k++){
printf("%02d : %02d : %02d\n",i,j,k);
Sleep(1000);
system("cls");//清除屏幕
}
}
}
??C语言日期函数?待补充
5.2 99乘法表
for(int i=1;i<9;i++){
for(int j=1;j<=i;j++){
printf("%d * %d = %d ",j,i,j*i);
}
printf("\n");
}
5.3 水仙花数字
5.4 猜数字
#include <time.h> // srand(time(NULL))
总是自动有个空格输入导致自动跳转无限循环 解决办法
srand(time(NULL));
int r_num=rand()%101+1; //1-100
int a;
while(1){
printf("请输入数字\n");
scanf("%d",&a);
while(getchar()!='\n'){
continue;
}
if(a==r_num){
printf("猜对了");
}
else{
printf("猜错了,是否继续, Y N\n");
char n;
scanf("%c",&n);
if(n=='N'){
printf("输入的字符是%c,表示您拒绝继续\n",n);
break;
}
}
}
5.5统计字符串中各个字母出现次数
char example[25] ="there is a big lake";
int countarr[26]={0};
//一个整数 数组,共26个数字,代表26个字母。初始化每个数都是零
for(size_t i=0;i<25;i++){
if(!isblank(example[i])){ //头文件cctype
int count=static_cast<int>(example[i]-'a');
countarr[count]++;
}
}
//printf("现在统计结果\n");
for(size_t j=0;j<26;j++){
if(countarr[j]!=0){
printf("%c字母在句子中出现次数是%d\n",j+'a',countarr[j]);
}
//j +'a'隐形数据类型转换,不放心的可以用(char)强制转
}