linux程序设计之文件标准IO

标准IO:

每个文件都有一个结构体 struct FILE ,来 记录自己的信息, in out

FILE

{

char *in; //输入缓存区

char *out;//输出的缓存区

}

文件本身都是存在 磁盘上,配备的缓存区在 内存中

有什么好处?

  1. 减少对磁盘的损耗

  2. 效率会提高,内存的速度快过于磁盘

大家知道有两个缓存区就行了,

缓存区有多大?

全缓存区: 默认情况

行缓存区:printf.

无缓存区: perror

FILE *fp;
fp = fopen("hello.txt","r");
if(fp==NULL)
{
perror("fopen");
//可以打印出错的原因
return -1;
}

  1. 标准IO 的函数接口:API函数

    打开,关闭,读,写,判断文件结束,冲洗fflush(清理缓存区的意思)

2.1 打开 fopen

FILE *fopen(const char *path, const char *mode);

const char *path: 要打开的文件的路径

const char *mode: 以什么方式打开

r : 以只读的方式打开,如果这个文件不存在,报错

打开文件之后,位置指针在 文件开头

r+ : 以读写的方式打开,其他跟 r 方式相同

w : 以只写的方式打开,如果文件不存在,创建

如果有,原来的内容会被清空,位置指针在 文件开头
w+: 以读写的方式打开,其他跟 w 方式相同

a: append 追加,只写打开,文件不存在,创建

打开后,位置指针在 文件末尾

a+: 与a相同,读操作的话,位置指针在文件开头

写操作的话,位置指针在文件末尾

返回值:

打开文件正确:返回 打开的文件的 FILE 结构体指针

打开失败: 返回NULL

下面三个 文件系统会自动打开,大家可以直接使用

FILE * stdin; 标准输入设备,一般是键盘

FILE *stdout; 标准输出设备,一般是屏幕

FILE *stderr; 标准出错设备,一般是屏幕

2.2 文件的关闭fclose
int fclose(FILE *fp);

参数: fp 前面用fopen打开的文件返回值;

有 fopen ,就需要有 fclose ,否则文件中数据,极有可能被破坏

2.3 文件的读写: 字符,行,格式化,普通

2.3.1 按照字节读: fgetc, getchar , 写 fputc, putchar

(1) fgetc:

     int fgetc(FILE *stream);
含义:读出stream 中的一个字节。

返回: 该字符的 ASCII  

当读出一个字符以后,位置指针会自动往后跳一个字节。

特殊字符:EOF (-1), 如果读到了 EOF 字符, 代表到了文件尾部.

(2) getchar

   int getchar(void);

含义: 从键盘读一个字符

返回: 该字符的 ASCII

getchar() <=======>fgetc(stdin)   

(3) fputc

int fputc(int c, FILE *stream);

含义: 把 c 字符,写到 stream 对应的 文件流中

FILE *fp;hg
char ch;
fp = fopen("1.txt","r");
if(fp==NULL)
{
perror("fopen");
return -1;
}
while((ch = fgetc(fp)) != EOF)
{
fputc(ch, stdout);
}

(4) putchar

int putchar(int c);

把 c 字符 输出到 屏幕:  等价于  fputc(ch, stdout);

main函数的运行参数:

argc: 参数的个数

char *argv[] : 字符串 数组, 保存的就是参数的列表

int main(int argc, char *argv[])
{
int i;
printf("argc=%d\n",argc);//main函数参数的个数
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
}

课堂练习:

fgetc, fputc, 实现将某个文件的第一行拷贝到一个新文件中,两个文件名 通过运行参数设置,

fcopybychar.c

3.3.2 按行: 读 fgets, gets 写: fputs, puts

(1) gets

char *gets(char *s);

含义: 从标准输入文件(键盘)读一行,存放在 s 指向的内存中,并在最后加上一个 '\0'

char buf[10];

gets(buf); 这个函数有隐患,会有bugs,现在基本不用这个函数

//从键盘输入 "hello world"

(2) fgets

char *fgets(char *s, int size, FILE *stream);

含义: 从 stream 文件流,可以是stdin, 读一行,读到 \n,或者是 EOF 就不读了

         存放在 s 所指向的内存中, size 要读的大小

         存放的时候,会在最后加上一个 '\0'

位置指针:挪到 还没读的 第一个字节。

如果一行没读完,下次会接着读。直到 '\n' 或者 EOF 为止

返回值: 正确 返回 s

返回NULL : 代表 读到了文件末尾,或者出错

例子:

int main(int argc, char argv[])
{
FILE *fpr,
fpw;
char buf[10];
int i;
fpr = fopen("1.txt","r");
if(fpr==NULL)
{
perror("fopen");
return -1;
}
fgets(buf,10,fpr);
for(i=0;i<10;i++)
{
printf("buf[%d]=%d\n",i,buf[i]);
}
}

(3) int puts(const char *s); //写到屏幕
int fputs(const char *s, FILE *stream);

含义: 把 s 所指向的内存的内容,写到 stream 对应的 流中

3.3.3 指定格式: 输入 scanf , fscanf, sscanf

                        输出    printf,   fprintf,  sprintf  

(1) fscanf:

         int fscanf(FILE *stream, const char *format, ...);
 含义: 从 stream 里  按照 format 读出数据

 参数: ...   若干个读出来的数据项 地址

返回值: 真正读出来的 数据项 个数.

有个文件:每行包含三项学生的数据:序号,姓名,成绩 

int main()
{
FILE *fp;
int id;
char name[20];
float score;
fp = fopen("stu1.dat","r");
if(fp==NULL)
{
perror("fopen");
return -1;
}
fscanf(fp,"%d%s%f",&id,name,&score);
printf("id=%d,name=%s,score=%f\n",id,name,score);
fclose(fp);
}

(2) sscanf

int sscanf(const char *str, const char *format, ...);

含义:从str 为首地址的存储空间 按照格式 读出数据.

下位机 发送数据到 上位机:一般 一串字符串:

str = “temp:30,humi:60,people:1”

char buf[20];

int temp, humi,peo;

sscanf(str,"%s:%d,%s:%d,%s:%d",buf,&temp,buf,&humi,buf,&peo)

if(temp>50)

报火警

if(peo==1)

报110

(3) fprintf

int fprintf(FILE *stream, const char *format, ...);

含义: 将... 的数据按format格式写入到文件stream中

返回:实际输出到文件中 的字符个数

练习: 从键盘输入 学号,姓名,成绩 到 新文件中 stu1.dat 中 , fprintf.c

int main()
{
int id,n;
char name[20];
float score;
FILE *fp;
fp = fopen("stu1.dat","w");
printf("please input no:");
scanf("%d",&id);
printf("please input name:");
scanf("%s",name);
printf("please input score:");
scanf("%f",&score);

n = fprintf(fp,"%d%s%f",id,name,score); //返回:实际输出到文件中 的字符个数
printf("n=%d\n",n);
fclose(fp);
}

(4) sprintf

int sprintf(char *str, const char *format, ...);

含义:将... 的数据按照 format 格式 写入 str 为首 的地址

返回值: 实际输出到 str 里的字节个数.

(5) snprintf

int snprintf(char *str, size_t size, const char *format, ...);
含义:与sprintf相同,多了一个参数 size 表示每次写多少个字节 -1。 (最后要存放一个 '\0')

注意:所谓的安全,可以控制size,别越界

%12d -----> 输出的宽度 是12个字节,前面补 空格

%012d------> 输出的宽度是12个字节, 前面补 0

int main () {
char a[16];
size_t i;

i = snprintf(a, 13, "%012d", 12345); // 第 1 种情况
printf("i = %lu, a = %s\n", i, a); // 输出:i = 12, a = 000000012345

i = snprintf(a, 9, "%012d", 12345); // 第 2 种情况
printf("i = %lu, a = %s\n", i, a); // 输出:i = 12, a = 00000001

return 0;
}

4.1 按任意结构读写: 以上的都是文本结构,所谓的文本文件 内容都是 ASCII 码编码的。 .c .txt

对于二进制文件, .bmp  .jpg  .exe  需要用 fread,  fwrite 来读写

(1) fread

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

含义: 从stream 文件流中读到信息,存放到 ptr 为首地址的 地方

参数:

ptr: 从stream文件流中 读到的信息存放到这里

size:读数据的 单位 (例如:每次读一个字节:1, 每次读一个学生结构体 sizeof(struct STU)

nmemb:读多少个size (上面的单位)

返回值: 真正读了多少个单位,不是字节数

读完之后,文件stream 位置指针 向后移动 (返回值*size) 个字节。

(2) fwrite

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

含义: 要将 ptr 位首地址的数据 写到 stream 文件中

参数: size : 要写的数的单位

  nmemb : 写多少个 单位

返回值: 真正写了多个个单位

文件的位置指针 向后移动 (返回值*size) 个字节。

学生的结构体:

typedef struct stu_info

{

 int id;

 char name[20];

 float score;

}STU;

STU stu[3], stu2[3];

课堂练习: 从键盘输入三个学生数据,存储到 stu中, 写入新文件 stu.dat .

      从stu.dat 读数据 fread存放到 stu2数据中, 

      显示到屏幕。

rewind(fp);

重新把位置指针 放到文件开头

typedef struct stu_info
{
int id;
char name[20];
float score;
}STU;

int main()
{
FILE *fp;
int i;
STU stu[3],stu2[3];

fp = fopen("stu.dat","w+");
for(i=0;i<3;i++)
{
printf("please input id score name\n");
scanf("%d%f%s",&stu[i].id,&stu[i].score,stu[i].name);
}
fwrite(stu,sizeof(STU),3,fp);
rewind(fp);
fread(stu2,sizeof(STU),3,fp);
for(i=0;i<3;i++)
{
printf("%d: %d %f %s\n",i+1,stu2[i].id,stu2[i].score,stu2[i].name);
}
}

4.2 如何判断文件是否结束 feof(), 不能再用 EOF (-1)

int feof(FILE *stream);
含义: 判断 stream 流 是否结束,

   如果结束 返回  非0  true

   如果未结束 返回 0   flase 

if/while( feof(fp)) 如果 fp结束 为 真

if/while(!feof(fp)) 如果 fp 没结束 为真

feof 不太稳定,已经结束了,判断还多一次. (因为 最后还有一次 EOF )

一般是先读 再判断 ,如果先判断 再读 就会多一次

  1. 冲洗函数 fflush

    int fflush(FILE *stream);

含义: 如果stream 是输入流,则 丢弃stream 的缓冲区的内容

                            某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,
                              但是并非所有编译器都要支持这个功能(linux 下的 gcc 就不支持),

    在scanf语句中间可以使用以下两行代码清除缓冲区,防止前面的错误影响后面的输入

    scanf("%*[^\n]");

    scanf("%*c");

 如果stream 是输出流,则 写入 stream 

fflush 在 scanf("%c") 之前 ,用的比较多, 避免某些按键,(回车,空格)作为char 类型输入的后面的变量中。

fflush(NULL); 是把该进程 所有打开的 流 同步

int main()
{
printf("hello world");
fflush(stdout); //强制把 输出缓存区的内存 写入 stdout

while(1);
}

  1. 定位函数 fseek ,想把 位置指针移动到 文件的任意位置

rewind(fp);

重新把位置指针 放到文件开头

int fseek(FILE *stream, long offset, int whence);
参数: stream 文件流

offset: 偏移多少字节, 正 往后移动, 100 往后移动 100 个字节

                                     负 往前移动,  -100 往前移动  100 个字节     

whence : 从哪里开始移动 (参照物)

   SEEK_SET  文件头

  SEEK_CUR  当前位置

  SEEK_END  文件尾

fseek(fp, 6, SEEK_SET); //定位到文件头6个字节的位置

fseek(fp, 0, SEEK_END); //定位到文件末尾

long ftell(FILE *stream);
含义: 告诉你,返回 stream 流的位置指针的 位置。

有什么方法可以获取到文件的大小?

fseek(fp, 0, SEEK_END); //定位到文件末尾

filelength= ftell(fp);

#创作者计划#
全部评论
感谢参与【创作者计划2期·技术干货场】!欢迎更多牛油来写干货,瓜分总计20000元奖励!!技术干货场活动链接:https://www.nowcoder.com/link/czz2jsghtlq(参与奖马克杯将于每周五结算,敬请期待~)
点赞 回复
分享
发布于 2021-03-04 14:23

相关推荐

11 10 评论
分享
牛客网
牛客企业服务