一. 标准IO
1. 标准IO函数有什么特点?
标准IO函数都是封装在一个库中,这个库就是标准C库,标准C库头文件都是#include <stdio.h>,所以标准IO函数的头文件都是#include <stdio.h>,而且标准IO处理方式与系统IO不一样,读取/写入数据时,都有缓冲区。
2. 系统IO与标准IO作用对象?
系统IO对象 -> 硬件设备文件,例如: 访问LCD液晶,触摸屏,红外传感器,温湿度传感器、超声波传感器
标准IO对象 -> 普通文件,例如: 访问1.txt,2.jpg,3.mp3...
二.如何使用标准IO来处理文件?
1. 访问文件? -> fopen() -> man 3 fopen
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
path: 需要访问的文件的路径
mode: 操作文件的权限
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|O_CREAT
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. //以读写方式打开文件,以追加方式写入文件,不存在则创建,如果是读操作,则从文件定位在文件开头,如果是写操作,则文件的定位在末尾。
返回值:
成功:文件指针
失败:NULL
2. 关闭文件? -> fclose() -> man 3 fclose
fclose - close a stream
#include <stdio.h>
int fclose(FILE *fp);
fp: 需要关闭的文件流指针
返回值:
成功:0
失败:EOF
#ifndef EOF
# define EOF (-1)
#endif
三. 关于默认打开的三个文件的文件指针。
头文件路径: /usr/include/unistd.h
文件描述符:
#define STDIN_FILENO 0 -> 标准输入的文件描述符
#define STDOUT_FILENO 1 -> 标准输出的文件描述符
#define STDERR_FILENO 2 -> 标准出错的文件描述符
文件指针:
extern struct _IO_FILE *stdin; -> 标准输入的文件指针
extern struct _IO_FILE *stdout; -> 标准输出的文件指针
extern struct _IO_FILE *stderr; -> 标准出错的文件指针
#define stdin stdin
#define stdout stdout
#define stderr stderr
===========================================================
总结:
文件描述符 文件指针
标准输入 STDIN_FILENO stdin
标准输出 STDOUT_FILENO stdout
标准出错 STDERR_FILENO stderr
练习1:测试fopen("w")中会不会创建,并关闭文件。
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("test.txt","w");
if(fp == NULL)
printf("fopen error!\n");
fclose(fp);
return 0;
}
四. 关于标准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
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
练习2: 验证读写返回值。
#include <stdio.h>
int main()
{
FILE *fp = NULL;
//char buf[50] = {0};
int n;
char wbuf[20] = "hello";
fp = fopen("test.txt","r+"); //17
if(fp == NULL)
printf("fopen error!\n");
//n = fread(buf,10,2,fp);
//printf("n = %d\n",n);//1
n = fwrite(wbuf,6,3,fp);
printf("n = %d\n",n);
fclose(fp);
return 0;
}
五. 关于标准IO文件指针定位函数
1. 重新定位文件指针 -> fseek() -> man 3 fseek
#include <stdio.h>
int fseek(FILE * stream, long offset, int whence);
stream: 需要进行偏移的文件指针
offset: 需要偏移的偏移量
whence: 基准点
SEEK_SET -> 文件开头
SEEK_CUR -> 文件当前位置
SEEK_END -> 文件末尾
返回值:
成功:0
失败:-1
2. 获取当前文件指针距离开头的绝对值
#include <stdio.h>
long ftell(FILE *stream);
stream:需要获取当前位置距离开头的字节数的文件指针
返回值:
成功: 距离开头的偏移量
失败: -1
3. 重置文件指针的定位到开头
#include <stdio.h>
void rewind(FILE *stream);
stream: 文件指针
返回值:无!
等价: fseek(stream, 0, SEEK_SET)
练习3: 使用文件IO中标准IO接口实现文件的拷贝
例如: ./xxx 1.txt 2.txt
要求1: 1.txt必须存在,并且不知道里面有多少个字节数
要求2: 2.txt不存在则创建,存在则清空
要求3: 无论1.txt中有多少个字节,拷贝完后,2.txt必须与1.txt一致。
#include <stdio.h>
int main(int argc,char *argv[]) // ./copy 1.txt 2.txt
{
char buf[10] = {0};
int n;
long a,b;
FILE *fp1 = fopen(argv[1],"r");
if(fp1 == NULL)
printf("fp1 error!\n");
FILE *fp2 = fopen(argv[2],"w");
if(fp2 == NULL)
printf("fp2 error!\n");
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(fp2);
fclose(fp1);
return 0;
}
六. printf()函数缓冲区问题。
printf()默认的输出对象是标准输出,是有缓冲区。这个缓冲区需要某些特定的条件才会输出数据。
printf()是一个行缓冲区函数,当遇到‘\n‘就会将之前的数据全部输出。
例子1:
int main(int argc,char *argv[])
{
printf("helloworld");
printf("yueqian");
return 0;
}
结果: 会输出helloworldyueqian
结论: 程序退出时,会刷新printf()函数的缓冲区。
例子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");
while(1);
return 0;
}
结果: 会输出helloworldyueqian
结论: 当遇到\n时,就会将缓冲区中所有内容都会输出。
例子4:
sleep(1); -> 进程挂起1秒
usleep(500000); -> 进程挂起0.5秒
int main()
{
while(1)
{
printf("helloworld");
usleep(100000);
}
return 0;
}
结果: 等到缓冲区满了,就会将缓冲区的数据全部输出。
结论: printf()缓冲区满了,即使程序不退出,也没有\n,也是会所有的内容都输出来。
例子5:
主动刷新数据缓冲区 -> fflush() -> man 3 fflush
NAME
fflush - flush a stream -> 刷新文件流指针
#include <stdio.h>
int fflush(FILE *stream);
stream:需要刷新的文件指针。
返回值:
成功:0
失败:-1
int main(int argc,char *argv[])
{
printf("helloworld");
fflush(stdout);
while(1);
return 0;
}
结果: 会输出helloworld
结论: 当执行fflush(stdout);刷新标准输出的缓冲区时,缓冲区中的内容就会全部输出。
例子6:
int main()
{
printf("pls input a num:");
scanf("%d",&a);
}
结果: 会输出pls input a num:
结论: 当调用IO函数,就会刷新缓冲区
例子7:
printf() -> 对象 -> 标准输出 -> 有缓冲区
-> 标准出错 -> 无缓冲区
fprintf() -> 修改输出对象
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
stream: 修改后的对象的文件指针
format: 输出的格式
printf("hello"); 等价于 fprintf(stdout,"hello");
int main()
{
while(1)
{
fprintf(stderr,"helloworld");
usleep(100000);
}
return 0;
}
结果: 每隔0.1S输出一个helloworld。
结论: 标准出错没有缓冲区。
总结printf()能输出的情况。
1. 程序退出
2. 遇到\n
3. 缓冲区满了
4. 主动使用fflush刷新缓冲区
5. 使用fprintf()修改输出对象
6. 再次调用IO函数刷新缓冲区
七. 关于标准IO特殊函数
getchar() getc() fgetc() putc() fputc() puts() fputs() fgets()
1. getchar() -> 阻塞从IO缓冲区中获取一个字符
#include <stdio.h>
int getchar(void);
参数:无
返回值:
成功:获取到的字符
失败:-1
例子:
#include <stdio.h>
int main()
{
int ret;
ret = getchar();
printf("%c\n",ret);
return 0;
}
注意:
getchar() 等价于 getc(stdin);
2. getc() fgetc() -> 阻塞从文件指针中获取一个字符。
#include <stdio.h>
int getc(FILE *stream);
stream: 文件指针
返回值:
成功: 获取到的字符
失败: -1
练习4: 使用getc将某个文件的内容一个一个字符地输出来!
#include <stdio.h>
int main()
{
/*
int ret;
ret = getchar();
printf("%c\n",ret);
*/
FILE * fp = fopen("test.txt","r");
while(1)
{
int ret = getc(fp);
if(ret == -1)
{
break;
}
printf("%c\n",ret);
}
fclose(fp);
return 0;
}
3. putc() fputc() -> 输出一个字符到屏幕上
#include <stdio.h>
int putc(int c, FILE *stream);
c: 输出的字符
stream: 文件指针
返回值:
成功: 写入的字符
失败: -1
4. puts() -> 输出字符串到屏幕上 -> man 3 puts
fputs() -> 输出字符串到文件指针上 -> man 3 fputs
int fputs(const char *s, FILE *stream);
int puts(const char *s);
s: 字符串的首地址
stream: 文件指针
返回值:
成功: 没有使用过的整型数据
失败: -1
puts(s) 等价于 printf("%s\n",s);
5. fgets() -> 从键盘中获取字符串 -> 包含\n在内
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
s: 缓冲区的地址
size: 字节数大小
stream:文件指针
返回值:
成功:指向s这个区域的地址
失败:NULL
#include <stdio.h>
int main()
{
char buf[50] = {0};
fgets(buf,50,stdin);
printf("buf = %s",buf);
if(strncmp(buf,"hello",5) == 0) 如果使用strcmp(),就不匹配!
{
printf("ok!\n");
}else{
printf("error!\n");
}
return 0;
}
原文地址:https://www.cnblogs.com/zjlbk/p/11252167.html