标准io&&getchar&&putchar

tech2022-09-15  94

一、标准IO。 1、标准IO有什么特点? 系统IO专门用于访问硬件设备的,标准IO专门用于访问普通文件。 标准IO中所有函数都是属于标准C库中的接口,所以标准IO的函数都是属于man手册中第3手册。

2、如何使用标准IO访问文件? 1)访问文件?  ->  fopen()  -> man 3 fopen 函数功能: stream open functions 头文件:#include <stdio.h> 原型:     FILE *fopen(const char *path, const char *mode);      参数:     path:需要打开的那个文件的路径  (绝对路径/相对路径)     mode:操作权限  -> 字符串  例如:"r"

 r      Open text file for reading.  The stream is positioned at the beginning of the file.     //以只读的方式来打开文件,文件的定位在最开头的。     O_RDONLY

 r+     Open for reading and writing.  The stream is positioned at the beginning of the file.     //以可读可写的方式来打开文件,文件的定位在最开头的。     O_RDWR

 w      Truncate file to zero length or create text file for writing.  The stream is positioned at the beginning of the file.     //以只写的方式打开文件,如果文件不存在就创建该文件,如果文件存在,则清空文件的内容,文件的定位在最开头的。     O_WRONLY|O_CREAT|O_TRUNC

 w+     Open for reading and writing.  The file is created if it does not exist, otherwise it is truncated.  The  stream  is  positioned at the beginning of the file.     //以可读可写的方式来打开文件,如果文件不存在就创建该文件,如果文件存在,则清空文件的内容,文件的定位在最开头的。     O_RDWR|O_CREAT|O_TRUNC

 a      Open  for  appending  (writing at end of file).  The file is created if it does not exist.  The stream is positioned at the end of the file.     //以只写追加的方式打开文件,写的话就在文件末尾开始写,文件不存在就会创建,文件的定位在末尾。     O_WRONLY|O_APPEND

 a+     Open for reading and appending (writing at end of file).  The file is created if it does not exist.  The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.     //以可读可写和追加的方式打开文件,写的话就在文件末尾开始写,文件不存在就会创建,如果读,就从文件开头读,如果写,就从文件末尾开始写。     O_RDWR|O_CREAT|O_APPEND

返回值:     成功:文件流指针  FILE *     失败:NULL

2)关闭文件。  -> fclose()  -> man 3 fclose 函数功能: fclose - close a stream 头文件:#include <stdio.h> 原型:     int fclose(FILE *stream);

参数:     stream: 需要关闭的那个文件的文件指针

返回值:     成功:0     失败:EOF

#define EOF -1

  练习1: 测试一下fopen()打开一个文件后,会不会占用一个文件描述符?   练习2: 打开一个文件,测试fopen第二个参数,并关闭文件。

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h>

int main(int argc,char *argv[]) {     FILE *fp = NULL;     fp = fopen("./test.txt","w");     if(fp == NULL)         printf("fopen error!\n");  -> 3          int fd = open("./kk.txt",O_RDONLY);     printf("fd = %d\n",fd);          fclose(fp); }

二、关于默认打开的三个文件的文件指针。 头文件:/usr/include/unistd.h

/* Standard file descriptors.  */ #define    STDIN_FILENO    0    标准输入的文件描述符 #define    STDOUT_FILENO    1    标准输出的文件描述符 #define    STDERR_FILENO    2    标准出错的文件描述符

头文件:/usr/include/stdio.h typedef struct _IO_FILE FILE;

/* Standard streams.  */ extern struct _IO_FILE *stdin;        标准输入的文件流指针   extern struct _IO_FILE *stdout;        标准输出的文件流指针 extern struct _IO_FILE *stderr;        标准出错的文件流指针

/* C89/C99 say they're macros.  Make them happy.  */ #define stdin stdin #define stdout stdout #define stderr stderr

总结:         文件描述符            文件指针 标准输入        STDIN_FILENO           stdin 标准输出        STDOUT_FILENO          stdout 标准出错        STDERR_FILENO          stderr

三、关于标准IO的读写操作。 1、如何读取文件?  -> fread()  -> man 3 fread 头文件:#include <stdio.h> 原型:        size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:     ptr: 数据缓冲区     size: 每一个块的字节数。      nmemb: 想读取的块数。        stream:需要读取的文件的文件描述符。

返回值:     成功:成功读取到的块数     失败:小于想读取的块数/0

文件总字节数:        30                             24                                 28        想读3块,每一块10个字节  -> 3     想读3块,每一块10个字节 -> 2         想读3块,每一块10个字节  -> 2        想读2块,每一块15个字节  -> 2     想读2块,每一块15个字节 -> 1         想读2块,每一块15个字节  -> 1        想读5块,每一块10个字节  -> 3     想读5块,每一块10个字节 -> 2         想读5块,每一块10个字节  -> 2                                            想读5块,每一块6个字节    -> 4   结论: 一定要读取到那一块数据的字节等于你想读取的块的字节时,才能算一块。     如果读取到那一块数据的字节小于你想读取的块的字节时,不能够算一块。

文本内容:helloworldhelloworldhelloworld 30个字节。 char buf[100] = {0}; fread(buf,10,3,fp);//3  //buf: helloworldhelloworldhelloworld

文本内容:helloworldhelloworldhell 24个字节。 char buf[100] = {0}; fread(buf,10,3,fp);//2  //buf:helloworldhelloworldhell

文本内容:helloworldhelloworldhell 24个字节。 char buf[100] = {0}; fread(buf,30,1,fp);//0  //buf: helloworldhelloworldhell  -> 虽然返回值为0,但是也能读取到东西。

文本内容:helloworldhelloworldhellowor 28个字节。 char buf[100] = {0}; fread(buf,10,3,fp);//2  //buf: helloworldhelloworldhellowor

  结论:不能直接通过返回值来判断读取到的内容是多少的。         fread(buf,10,3,fp);

返回值为2  -> 不能说就读取到了20个字节,我们只能判断出了读到20个/20个以上,但是一定是30个以下。                   但是读到20几个?我们不知道。

2、写入文件?  -> fwrite()  -> man 3 fwrite 头文件:#include <stdio.h> 原型:     size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    ptr: 需要写入到文件中的内容。     size:  每一个块的字节数。     nmemb: 想写入的块数。      stream: 目标文件的文件指针。

返回值:     成功:写入的块数。      失败:小于想写入的块数/0。 

char buf[] = "helloworldhelloworldhelloworld"   -> 30

fwrite(buf,10,3,fp); 返回值:3 文件内容:helloworldhelloworldhelloworld

fwrite(buf,15,2,fp); 返回值:2 文件内容:helloworldhelloworldhelloworld

fwrite(buf,10,5,fp); 返回值:5 文件内容:helloworldhelloworldhelloworld + 1个空格 + 19个乱码

fwrite(buf,6,5,fp); 返回值:5 文件内容:helloworldhelloworldhelloworld

fwrite(buf,5,5,fp); 返回值:5 文件内容:helloworldhelloworldhello

fwrite(buf,20,1,fp); 返回值:1 文件内容:helloworldhelloworld

fwrite(buf,40,1,fp); 返回值:1 文件内容:helloworldhelloworldhelloworld + 1个空格 + 9个乱码

  结论: fwrite()你想写多少块,返回值就是多少,如果内容不够,就乱码来凑。

   练习3:  先读,再读,是继续读,还是重新读?             先写,再写,是继续写,还是重新写?         先读,再写,是继续写,还是重新写?         先写,再读,是继续读,还是重新读?   -> 四个都是继续。

#include <stdio.h>

int main(int argc,char *argv[]) {     //1. 访问文件     FILE *fp = NULL;     fp = fopen("./ggy.txt","r+"); //helloworld     if(fp == NULL)         printf("fopen error!\n");          //2. 先读一些内容     char buf[10] = {0};     fread(buf,3,2,fp);       printf("buf = %s\n",buf); //buf:hellow          //3. 再写一些内容进去     fwrite("appletree",4,2,fp); //文件的内容: hellowappletre          //4. 关闭文件     fclose(fp);          return 0; }

   结论: 标准IO读写操作会影响到文件的偏移量,而且打开文件时,默认文件偏移量在文件的开头。

四、关于重新定位的问题。 1、重新定位文件流指针。  -> fseek()  -> man 3 fseek 功能: reposition a stream     //重新定位流指针

头文件:#include <stdio.h> 原型:     int fseek(FILE *stream, long offset,  int whence);

参数:     stream: 需要发生偏移的文件指针。     offset: 需要偏移的字节数。     whence: SEEK_SET  -> 相对于文件的开头。          SEEK_CUR  -> 相对于当前的位置。          SEEK_END  -> 相对于文件的末尾。

返回值:     成功:0     失败:-1

2、获取当前位置距离开头的偏移量。 -> ftell()  -> man 3 ftell 头文件:#include <stdio.h> 原型:     long ftell(FILE *stream);

参数:     stream:文件流指针。

返回值:     成功:距离文件开头的偏移量     失败:-1

3、重置文件指针。  -> rewind() -> man 3 rewind 头文件:#include <stdio.h> 原型:       void rewind(FILE *stream);

参数:     stream:文件流指针。

返回值:无

   例子: 写一个代码,试试以上几个函数。

#include <stdio.h> #include <strings.h>

int main(int argc,char *argv[])                                                           {     FILE *fp = fopen("./ggy2.txt","w+");     if(fp == NULL)         printf("fopen error!\n");          char buf[10] = "appletree";     fwrite(buf,4,2,fp);          rewind(fp);          char buf2[20] = {0};     int n = fread(buf2,10,2,fp);     printf("n = %d\n",n);  //0     printf("buf2 = %s\n",buf2); //appletre          char *p = "helloworld";     n = fwrite(p,5,2,fp);     printf("n = %d\n",n);//2          long a = ftell(fp);     printf("a = %ld\n",a);//18          fseek(fp,-13,SEEK_CUR);          bzero(buf2,sizeof(buf2));     fread(buf2,4,2,fp);     printf("buf2 = %s\n",buf2);//trehello                 //文件内容:appletrehelloworld     fclose(fp);          return 0; }

练习4: 假设当前目录下有一个1.txt的文件,里面有一些内容,但是我们不知道里面有多少个字节,要求大家写一个程序copy.c        当执行:./copy 1.txt 2.txt 时。2.txt会自动创建,2.txt的内容与1.txt完全一致。使用标准IO来完成。

#include <stdio.h>

int main(int argc,char *argv[]) //./p4 1.txt 2.txt {     FILE *fp1 = fopen(argv[1],"r");     FILE *fp2 = fopen(argv[2],"w");          char buf[10] = {0};     int n;     long a,b;     while(1)     {         a = ftell(fp1);                  n = fread(buf,5,2,fp1);         if(n == 2)         {             fwrite(buf,10,1,fp2);         }                  if(n < 2) //最后一次         {             b = ftell(fp1);             fwrite(buf,b-a,1,fp2);             break;         }     }          fclose(fp1);     fclose(fp2);          return 0;     }

五、printf()输出问题。 printf()输出对象默认是标准输出,标准输出是有缓冲区的,也就是说这些缓冲区需要特定的条件才会输出数据。 printf()是一个行缓冲函数,就是只要在缓冲区遇到'\n',就会把'\n'之前的数据全部输出。

例子1: int main(int argc,char*argv[]) {     printf("helloworld");     printf("yueqian");     return 0; } 结果:helloworldyueqian 结论:当程序退出时,会刷新标准输出缓冲区。

例子2: int main(int argc,char*argv[]) {     printf("helloworld");     printf("yueqian");     while(1);     return 0; } 结果:不会输出任何内容。 结论:当程序不退出时,是不会刷新标准输出缓冲区。

例子3: int main(int argc,char*argv[]) {     printf("helloworld");           printf("yueqian\n");     printf("appletree");     while(1);     return 0; } 结果:helloworldyueqian 结论:当遇到'\n'时,就会将之前的数据全部输出。

例子4: int main(int argc,char*argv[]) {     while(1)     {         printf("hello");         usleep(100000); //0.1S      }     return 0; } 结果:等到缓冲区满了,就会将缓冲区的数据全部输出。 结论:printf()如果缓冲区满了,即使程序没有结束,也没有'\n',也是会全部输出的。

例子5: int main(int argc,char*argv[]) {     int x;     printf("helloworld");      scanf("%d",&x);          while(1);     return 0; } 结果:输出helloworld。 结论:键盘stdin会刷新标准输出。

例子6:主动刷新缓冲区。  -> fflush()  -> man 3 fflush 功能: flush a stream     //刷新一个流

头文件:#include <stdio.h> 原型:     int fflush(FILE *stream);

参数:     stream: 需要刷新的流指针。  -> stdout

返回值:     成功:0     失败:EOF

#include <stdio.h> #include <unistd.h> int main(int argc,char*argv[]) {     printf("helloworld");      printf("yueqian");     fflush(stdout);     while(1);     return 0; } 结果:helloworldyueqian 结论:fflush会主动刷新缓冲区。

例子7:修改printf()的输出对象为标准出错,因为标准出错是没有缓冲区的,所以往标准出错上写入到数据,有什么数据就会输出什么。 修改输出对象:fprintf()  -> man 3 fprintf

头文件:#include<stdio.h> 原型:     int fprintf(FILE *stream, const char *format, ...);

参数:     stream: 输出对象    -> stderr     format: 输出内容

  printf("hello");  等价于 fprintf(stdout,"hello");  -> 将hello这个字符串放在标准输出的缓冲区上。                fprintf(stderr,"hello");  -> 将hello这个字符串放在标准出错上。

#include <stdio.h> #include <unistd.h> int main(int argc,char*argv[]) {     fprintf(stderr,"helloworld");      while(1);     return 0; } 结果:输出helloworld 结论:标准出错没有缓冲区。

六、标准IO函数。 sprintf()  getchar()  getc()  fgetc()  putchar()  putc()  fputc()  puts()  gets()  fgets()

1、sprintf()  -> 保存一个字符串,拼接字符串。  -> man 3 sprintf 头文件:#include <stdio.h> 原型:     int sprintf(char *str, const char *format, ...);

参数:     str:数据缓冲区     format:字符串格式

返回值:     字符串总字节数。

int a = 10; printf("a = %d\n",a);  -> 将a的值放置到%d的位置,然后将"a = 10\n"这句话输出到标准输出上。

int a = 10; char buf[30] = {0}; sprintf(buf,"a = %d\n",a);  -> 将a的值放置到%d的位置,然后将"a = 10\n"这句话保存到数组buf中。                 -> buf必须足够大去接受字符串。     

2、 getchar()  -> man 2 getchar   (用于清空缓冲区) 函数只能从标准输入缓冲区中获取一个字符 头文件:#include <stdio.h> 原型:     int getchar(void);

返回值:     成功:获取到的字符。     失败:EOF

一般地: 我们使用完scanf()后,都应该主动清空缓冲区。 例如: scanf(); while(getchar()!='\n');

例子1: char a; int b; scanf("%d",&b); scanf("%c",&a);   -> 因为回车还在缓冲区中,然后被%c拿走了。 printf("a = %d\n",a); printf("b = %d\n",b);

运行结果: 输入了 50 + 回车 a = 10 b = 50

例子2: char a; int b; scanf("%d",&b); getchar();  -> 回车被拿走了,这时候缓冲区是空白的 scanf("%c",&a);   -> 由于缓冲区是空白,所以阻塞。 printf("a = %d\n",a); printf("b = %d\n",b);

运行结果: 输入了 50 + 回车  c + 回车 a = 99 b = 50

例子3: char a; int b; scanf("%d",&b);  -> 拿走了10 getchar();       -> 拿走了c scanf("%c",&a);  -> 拿走了3 printf("a = %d\n",a); printf("b = %d\n",b);

运行结果: 输入了 10c30 + 回车 a = 51  -> '3' b = 10

例子4: char a; int b; scanf("%d",&b);          -> 拿走了10 while(getchar()!='\n');  -> 清空整个缓冲区  -> 一直获取,知道获取到\n为止。 scanf("%c",&a);          -> 由于缓冲区是空白,所以阻塞。  -> 输入c获取c printf("a = %d\n",a); printf("b = %d\n",b);

运行结果: 输入了 10c30 + 回车  c + 回车 a = 99 b = 10

3、getc()  -> man 3 getc 功能: 从一个流中获取一个字符。 头文件:#include <stdio.h> 原型:int getc(FILE *stream);      int fgetc(FILE *stream);

参数:     stream:需要获取字符的文件的文件指针。

返回值:     成功:获取到的字符     失败:EOF

1) getc() is equivalent to fgetc()            等价于 2) getchar()  -> 只能从stdin获取。 getc()     -> 可以从任何的流获取。   getc(stdin)     -> getc会从stdin中获取字符  等价于  getchar()                       fp = fopen("1.txt");                          getc(fp);       -> getc会从fp中获取字符

4、 putchar()  -> man 3 putchar   功能: 将一个字符放置到标准输出的缓冲区中。 头文件:#include <stdio.h> 原型:     int putchar(int c); 参数:     c: 需要放置到标准输出缓冲区的字符

返回值:     成功:返回写入到标准输出缓冲区的字符     失败:EOF

其实: putchar('x');  等价于 printf("%c",'x');

5、putc()  -> man 3 putc 头文件:#include <stdio.h> 原型:     int fputc(int c, FILE *stream);     int putc(int c, FILE *stream);

参数:     c:需要放置的字符。     stream:需要放置的目标的流。

返回值:     成功:返回写入到目标缓冲区的字符     失败:EOF

1) putc() is equivalent to fputc()      等价于

2) putchar('x')  等价于  putc('x',stdout);

    例题:使用getc与putc完成文件拷贝。

#include <stdio.h>

int main(int argc,char *argv[])  //ggy.txt  -> ggy4.txt {     FILE *fp1 = fopen("./ggy.txt","r");     FILE *fp2 = fopen("./ggy4.txt","w");          int a;     while(1)     {         a = getc(fp1);         if(a == -1)         {             break;         }              putc(a,fp2);     }          return 0; }

6、puts()  -> man 3 puts       puts(s) 等价于 printf("%s\n",s); 功能: 将一个字符串+一个'\n'搞到标准输出上。 头文件:#include <stdio.h> 原型:     int puts(const char *s);

参数:     s: 需要搞到标准输出上的字符串。

返回值:     成功:非负整型数据   字符个数+‘\n’     失败:EOF      7、fputs()  -> man 3 fputs 功能:将字符串s放置到一个流上  (不包含'\n') 头文件:#include <stdio.h> 原型:int fputs(const char *s, FILE *stream);

    s: 字符串         stream: 目标的流    返回值:     成功:非负整数  1     失败:EOF

8、fgets()  -> man 3 fgets   (包含'\n'在内) 头文件:#include <stdio.h> 原型:      char *fgets(char *s, int size, FILE *stream);    参数:     s:缓冲区     size:最大获取的字节数     stream:获取的目标    

返回值:     成功:s的地址     失败:NULL

1、完成昨晚的第3题。 2、修改项目中系统IO为标准IO。

3、把项目中错误信息改成刷颜色。

4、究竟小孩退出登录时,要不要强制保存小孩的数据。    家长  -> 修改了小孩的年龄,密码之类  -> 需要保存到文件中。

最新回复(0)