C语言复习——文件操作以及各种输入输出

翁老师说现在很少有人直接用C语言来操作文件来储存或者读取数据了,稍微有点量的用数据库。个人觉得C语言实现文件操作还是挺有意思的。做个练习——一个简单的学生信息系统

头文件 

#ifndef _STUDENT_H_
#define _STUDENT_H_

#define STR_SIZE 20
/*声明学生结构体*/
typedef struct _student{
	int id;
	char name[STR_SIZE];
	int gender;
	int age;
}STUDENT;
#endif

void getlist(STUDENT *std,int num);//读入学生信息 
int save(STUDENT *std,int num);//储存至文件 
void read(FILE *fp,int cnt);//读取数据 

main函数,确定学生数量,调用getlist(),用数组存储(也可用链表);调用save(),将数据保存至文件;调用read(),读取数据。

#include <stdio.h>
#include <stdlib.h>
#include "student.h"

int main(int argc, char *argv[]) {
	int num=0;
	printf("请输入学生数量:");
	scanf("%d",&num);
	STUDENT std[num];//学生结构数组 
	getlist(std,num);//读取数据,也就是给给各元素的成员赋值 
	printf("是否保存数据?(1-保存,0-取消)");
	int flag=-1;
	scanf("%d",&flag);
	if(flag){
		if(save(std,num)){
			printf("保存成功\n");
		}else{
			printf("保存失败\n");
		}
	}
	/*读取*/
	FILE *fp=fopen("student.date","r");
	if(fp){
//fseek()函数可将文件指针指向某位置,SEEK_SET-文件头,SEEK_END-文件末,SEEK_CUR-文件当前位置,第二个参数是偏移字节数,+-决定偏移方向		
		fseek(fp,0,SEEK_END);
//ftell()函数,定位文件指针位置,返回文件头到该位置的字节数
		int num=ftell(fp)/sizeof(STUDENT);
		printf("共有%d个数据,要看第几个?",num);
		int cnt;
		scanf("%d",&cnt); 
		read(fp,cnt);
		fclose(fp);
	}
	return 0;
}

分别用二进制模式和文本模式写入、读取文件

void getlist(STUDENT *std,int num){
	char format[STR_SIZE];
	sprintf(format,"%%%ds",STR_SIZE-1); //利用格式化输出,做一个格式字符串“%19s” 
	for(int i=0;i<num;i++){
		printf("请输入第%d个学生的信息\n\t学号:",i+1);
		scanf("%d",&std[i].id);
		printf("\n\t姓名:");
		scanf(format,std[i].name);//格式字符串的地址替代“%s”
		printf("\n\t性别(0-女,1-男):");
		scanf("%d",&std[i].gender);
		printf("\n\t年龄:");
		scanf("%d",&std[i].age);
	}
}
int save(STUDENT *std,int num){
	int ret=0;
	FILE *fp=fopen("student.date","w");
	if(fp){
		ret=fwrite(std,sizeof(STUDENT),num,fp);//二进制写入文件 
//文本写入文件
//		for(int i=0;i<num;i++){ 
//			fprintf(fp,"学号:%d",std[i].id);
//			fprintf(fp,"\n姓名:%s",std[i].name);
//			fprintf(fp,"\n性别:%s",(std[i].gender==1?"男":"女"));
//			fprintf(fp,"\n年龄:%d",std[i].age);
//			fprintf(fp,"\n");
//		}
//		ret=1;//文本模式下赋值1,代表执行到此,写入成功 
		fclose(fp);
	}else{
		printf("文件打开失败!");
	}
//	return ret;
	return ret==num;//二进制写入文件fwrite()返回值为写入 某个结构体(某大小字节块) 的个数 
}
/*读取文件内数据*/
void read(FILE *fp,int cnt){
//以文本模式读取文件内容,一次读入100个字符,fgets遇行末‘\0’停止、遇到换行停止。 	
//	char s[100];
//	while(fgets(s,100,fp)){
//		printf("%s\n",s);
//	}
//以二进制模式读取文件内容,可用fseek()函数定位至第几个结构。 
	STUDENT std;
	fseek(fp,(cnt-1)*sizeof(STUDENT),SEEK_SET);
	fread(&std,sizeof(STUDENT),1,fp);
	printf("学号:%d\n",std.id);
	printf("姓名:%s\n",std.name);
	printf("性别:%s\n",std.gender==1?"男":"女"); 
	printf("年龄:%d\n",std.age);
}

 注意:r+和w+的读写模式,在第一次读取或者写入后,文件指针会指向读取或者写入的当前位置,无法继续进行反向操作。只有rewind()重置指针位置至文件头才能继续。

w和w+会清空原文件内容;

a和a+是在文件末追加内容,不会清空原文件内容;

r+会覆盖原文件内容。

补充知识点,格式化输入输出:

一、格式化输出

printf("%d",i)中,%d为格式化输出字符串在%和d中间可以添加4种参数:

%[flag][width][.prec][hil]type

1、flag

flag含义
-左对齐
+显示+-符号
空格正数留空
00填充所占位数

例:

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char const *argv[]){
	int a=50,b=-100;
	printf("|%-+9.5d|\n|%+-9.4d|\n",a,b);
	system("pause");
	return 0;
}

2、width和.prec

width/.prec含义
number输出所占字符数
.number

对于float保留几位小数

对于int是填补0后共占用几个字符数

*将number作为参数替代字符串中的*
.*将.number作为参数替代字符串中的.*

例: 

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char const *argv[]){
	float a=50,b=-100;
	printf("|%-+*.*f|\n|%+-*.*f|\n",9,6,a,9,5,b);
	system("pause");
	return 0;
}

 

由此可得当[width]和[.prec]冲突时,以[.prec]为主,width自动增加。

3、HIL

类型修饰(HIL)含义
hh以单字节形式输出
hshort
llong
lllong long
Llong double

hh以单字节形式输出:例如一个整形占4个字节,hh将最小字节上的内容输出。

例:12345 二进制最小字节内容为0011 1001,则输出为57。

但:我的电脑hhd单字节格式输出不正常, 不知道什么原因。。。

        当printf("%hhd",(char)a);输出单字节整数时,对a进行强制类型转换,输出57正确。

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char const *argv[]){
	int a=12345;
	printf("%i\n",a);//a可以是8/10/16进制的任何一种 
	printf("%u\n",a);//unsigned 无符号整形 
	printf("%o\n",a);//八进制整形 
	printf("%x\n",a);//十六进制整形 
	printf("%hhd\n",(char)a);//单字节整形 
	unsigned int mask=1;
	for(mask=1u<<31;mask;mask>>=1){//二进制
		printf("%d",a&mask?1:0); 
	}
	return 0;
}

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char const *argv[]){
	int a=54321;
	printf("%hhi\n",a);//a可以是8/10/16进制的任何一种 
	printf("%hhu\n",a);//unsigned 无符号整形 
	printf("%#o\n",a);//八进制整形 #可以显示8和16进制的前缀 
	printf("%#x\n",a);//十六进制整形 
	printf("%hhd\n",(char)a);//单字节整形 
	unsigned int mask=1;
	for(mask=1u<<31;mask;mask>>=1){//二进制 
		printf("%d",a&mask?1:0); 
	}
	return 0;
}

 4、利用格式化输出可以自己制作格式化输出字符串。

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define len 20
int main(int argc,char const *argv[]){
	float a=50,b=-100;
	char format[len];
	sprintf(format,"%%%ds",len-1);//自定义一个格式化字符串"%19s"输出到format里面.
	printf("%s\n",format);
	char name[len];
	scanf(format,name);
	printf("%s\n",name);
	return 0;
}

 二、格式化输入

scanf("%d",&a);

%[flag]type

flag含义flag含义
*跳过llong
number输入最大字符数lllong long
hhcharLdouble long
hshort
type含义type含义
dinta/e/f/gfloat
i可8可10可16cchar
uunsigned ints字符串
o8进制p指针
x16进制[^。]。前的所有字符

*跳过输入指定格式;i的输入格式可为10、8、16三种进制

例:

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char const *argv[]){
	int num;
	scanf("%*d%i",&num);
	printf("%d",num);
	return 0;
}

#include <stdio.h>
#include <stdlib.h>
#define len 20
int main(int argc,char const *argv[]){
	char str[len];
	scanf("%*[^.].%[^.].",str);
//%*[^.].跳过.前面所有字符并读掉.;%[^.].读取.前面的所有字符放入str,并读掉.
	printf("%s",str);
	return 0;
}

  

三、scanf和printf的返回值 

scanf返回值为整形,返回读取的变量个数。

printf返回值为整形,返回输出的字符个数。

#include <stdio.h>
#include <stdlib.h>
#define len 20
int main(int argc,char const *argv[]){
	char str[len];
	int a=scanf("%*[^.].%[^.].",str);
	int b=printf("%s\n",str);
	printf("a:b=%d:%d",a,b);
	return 0;
}

a为读取的变量个数,在程序中第一个字段跳过,第二个字段存入了str也就是读取了一个字符数组。b为输出的字符个数,在程序中输出了str,也就是san以及\n换行共4个字符。 

四、其他输入输出方式

C语言除了scanf和printf外,还有getchar和putchar用于读取和输出单个字符;gets和puts用于读取和输出字符串;fgets和fputs也是用来读取和输出字符串;fscanf和fprintf用来和文件交互,读取或者输入。用法如下:

1、int c=getchar(void)读入单个字符,存为int型;int num=putchar(c),输出单个字符,返回该字符的int型值。

2、char *str=gets(char *str);读入字符串,存入str.该函数遇到换行符结束读取,会将换行符读掉,并在字符串末尾添加\0;该函数无法控制读取内容长度,存在越界风险。

      int n=puts(char *str);输出字符串,自动换行,返回非负整数表示输出成功,EOF表示失败。

3、char *str=fgets(char *str,int ,FILE *);从文件中或者缓冲区stdin读取字符串,存入str字符数组中,读取长度为str长度-1,自动添加\0终止符。遇到\n终止读取,如果\n在字符数组长度范围内,换行符会被存入str并以\0结尾,如果\n刚好落在字符数组最后一位,将被转换为\0。当缓冲区字符串长度超过str长度,剩余的字符会留在缓冲区。

       int n=fputs(char *str,FILE*);向文件或者输出流输出字符串str,不会自动换行,返回非负整数表示成功,EOF表示失败。

4、int n=fscanf(FILE*,格式化字符串,存入地址);从文件流或者输入流读取内容,可以是任意类型格式化字符串,存入相应的变量地址,返回值是读取变量的数量。当读取字符串时,遇到任意空字符(空格、制表符、换行符)均结束读取,空字符依然存在流中。返回值为读取数据个数,失败或者结尾返回EOF。

      int n=fprintf(FILE*,format,变量名);向文件或者缓冲区输出数据,可以是任意类型变量,配合相应的格式化字符串。当输出字符串时,遇到\0结束,返回值为输出字符个数,失败返回EOF。

5、int getc(FILE *stream)和int putc(FILE *stream)两个函数,基本功能和getchar()/putchar一样,不仅用于终端单个字符的读取和输出,还能用于文件中单个字符的输入和输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值