缓冲输入和输出
1.用户缓冲:需要对普通文件指向许多轻量级I/O请求的程序通常使用用户缓冲I/O。
用户缓冲I/O是在用户空间而不是在内核中完成的。
主旨:提高操作效率: 用户空间程序 dd
dd bs=1 count=2097152 if=/dev/zero of=pirate
dd bs=1024 count=2048 if=/dev/zero of=pirate
2.块大小:一般为512,1024,2048字节。
3.标准I/O:C标准库。
4.文件指针:标准I/O并不操作文件描述符,而是操作文件指针 file pointer.
标准库中,文件指针映射到文件描述符,文件指针由FILE类型的指针表示,FILE类型定义在<stdio.h>中。
在标准I/O中,一个打开的文件叫做"流”。
打开文件操作流: fopen():打开指定路径的文件,并为它关联一个新的流。
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
打开失败返回一个合法的FILE 指针,否则返回NULL指针,并设置相应的 errno。
例: 打开文件 /etc/manifest
FILE *stream;
stream=fopen("/etc/manufest","r");
if(!stream)
/*error*/
通过文件描述符打开文件: fdopen 将已经打开的文件描述符转化成一个流。
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);
一旦文件描述符被转换成一个流,则不应该直接在该文件描述符上进行I/O。
例:打开文件/home/dirname/filename.txt,并创建关联的流
FILE *stream;
int fd;
fd=open("/home/dirname/filename.txt",O_RDONLY);
if(fd == -1)
/*error*/
stream=fdopen(fd, "r");
if(!stream)
/*error*/
关闭流:fclose,fcloseall:关闭给定的流,也会关闭相关给定的文件,所有被缓存但还没有写出的数据会被先写出。成功返回0,失败返回EOF,设置相应的errno。。
#include<stdio.h>
int fclose(FILE *stream);
成功返回0, 失败返回EOF,且设置相应的errno。
关闭所有的流:fcloseall(): 关闭所有的和当前进程相关联的流,包括stdin,stdout,stderr。
#define _GNU_SOURCE
#include <stdio.h>
int fcloseall(void);
从流中读取数据:多种方式“单字节,单行,二进制读取。需要流以恰当的方式打开。
#include <stdio.h>
int fgetc(FILE *stream); //函数从流中读取下一字节,并把该无符号字符强制转为int返回。强转是为了有足够的范围来表示文件结尾符和错误。
例:stream:必须以可读模式打开。
int c ;
c=fgetc(stream);
if(c== EOF)
/*error*/
else
printf("c= %c\n",(char)c);
把字符回放入流中:不需要的字符,可以将它放回。
#include <stdio.h>
int ungetc(int c, FILE *stream);
每次调用把 char 强制转成一个无符号字符并放回流中,成功时,返回c,失败返回EOF,随后读取流会返回c。FILO的方式读出。
按行的读取:fgets()
#include <stdio.h>
char *fgets(char *str, int size, FILE *stream);
从流中读取size-1个字节的数据,并把数据存入str中,
例:
char buf[LINE_MAX];
if(!fgets(buf, LINE_MAX,stream))
/*error*/
读取任意字符串:用fgetc 实现fges() 的功能
例:
char *s;
int c;
s = str;
while(--n > 0 && (c = fgetc(stream)) != EOF)
*s++=c;
*s='\0';
例: 将上述在指定文革符d 处停止
char *s;
int c;
s = str;
while(--n > 0 && (c = fgetc(stream)) != EOF && (*s++=c)!= d)
;
if(c == d)
*--s = '\0';
else
*s='\0';
读取二进制文件:
#include <stdio.h>
size_t fread(void *buf, size_t size, size_t nr, FILE *stream);
例:从给定流中读取一个线性大小的元素
char buf[64];
size_t nr;
nr = fread(buf, sizeof(buf), 1, stream);
if(nr == 0)
/*error*/
5.向流中写入数据:单个字节的写入,字符串写入,二进制写入。
写入成功返回写入个数,写入失败写入EOF。
写入单个字符:fputc()
#include <stdio.h>
int fputc(int c,FILE *stream);
例:
if(fputc('p', stream) == EOF)
/*error*/
写入字符串:fputs()
#include <stdio.h>
int fputs(const char *str,FILE *stream);
例:
stream = fopen("testfile.txt","a");
if(!stream)
/*error*/
if(fputs("abcdefg,hijklmn,xx.\n",stream) == EOF);
/*error*/
if(fclose(stream) == EOF)
/*error*/
写入二进制数据
#include <stdio.h>
size_t fwrite(void *buf, size_t size, size_t nr, FILE *stram);
将buf指向的nr个元素,每个元素长 size 写入到stream 中。
缓冲I/O实例程序:
/***********/
#include <stdio.h>
int main(int ac, char *av[])
{
FILE *in, *out;
struct pirate
{
char name[100];
unsigned long booty;
unsigned int beard_len;
}p, blackbeard={"Edward Teach", 905,48};
out = fopen("data", "w");
if(!out)
{
perror("fopen");
return 1;
}
if(!fwrite(&blackbeard,sizeof(struct pirate), 1, out))
{
perror("fwrite");
return 1;
}
if(fclose(out))
{
perror("fclose");
return 1;
}
in = fopen("data", "r");
if(!in)
{
perror("fopen");
return 1;
}
if(!fread(&p, sizeof(struct pirate), 1, in))
{
perror("fopen");
return 1;
}
if(fclose(in))
{
perror("fclose");
return 1;
}
printf("name = \"%s\" booty = %lu beard_len=%u\n", p.name, p.booty, p.beard_len);
return 0;
}
/***********/
7.定位流:控制当前流的位置:fseek():操纵流指向文件中由 offset 和 whence 指向的位置。成功返回0,失败返回-1,并设置相应 errno的值。
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
int fsetpos(FILE *stream, fpos_t *pos); //将函数流的位置设置到 pos 处。
void rewind(FILE *stream);
将流重置到流的初始位置
rewind(stream) 等价于 fseek(stream, 0, SEEK_SET);
例:
errno = 0;
rewind(stream);
if(errno)
/*error*/
获取当前流的位置:ftell(): 成功时返回流当前位置, 错误时返回-1,并设置相应的 errno
#include <stdio.h>
long ftell(FILE *stream);
int fgetpos(FILE *stream, fpos_t *pos);
成功时,返回0,并将当前流的位置设置为pos。失败返回-1,并设置相应 的 errno.
清洗一个流:fflush(): 将用户缓冲区的数据写入内核。成功时返回0, 失败返回EOF,并设置errno。
#include <stdio.h>
int fflush(FILE *stream);
fflush 只是把用户缓冲的数据写入到内核缓冲区,并不保证数据能够写入物理介质。
更多的是在调用fflush 之后,需要调用 fsync()。
9.错误和文件结束:一些标准的I/O接口,如fread,向调用者传递信息的能力很差,没有区分错误和EOF的机制。
两个函数用于区分流出现了错误还是到达了文件结尾。
#include <stdio.h>
int ferror(FILE *stream );
如果函数被设置了错误标志,函数返回非0值,否则返回0.
int feof(FILE *stream);
测试文件结尾标志是否被设置,结尾被设置,返回非零值,非则返回0
oid clearerr(FILE *stream);
清错误函数,需要在调用 ferror() 或者 feof() 之后才能调用。
例:
/*f 是一个可用的文件流*/
if(ferror(f))
printf("Error on f\n");
if(feof(f))
printf("EOF on f\n");
clearerr(f);
10.获得关联的文件描述符:当关联的I/O函数并不存在的时候,可以通过调用流的文件描述符对流执行系统调用
#include <stdio.h>
int fileno(FILE *stream);
最好在执行获取文件描述符之前对流进行冲洗。
11.用户I/O控制:
1.不缓冲:数据直接提交到内核
2.行缓冲:每遇到换行符,数据提交到内核。
3.块缓冲:全缓冲。
控制使用的 缓冲类型函数:
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
mode:缓冲模式。
setvbuf:必须在打开流之后,执行任何操作之前调用。成功是返回0,失败返回非零值。
12.线程安全:线程就是在同一个进程中执行的多个实例。
线程的定义:贡献同一个地址空间的多个进程。
标志I/O为独立操纵和流关联的锁提供了一系列的函数
手动文件加锁:flockfile():等待流被解锁,然后获得锁,增加锁计数,成为流的所有者线程,然后返回。
#include <stdio.h>
void flockfile(FILE *stream);
void funlockfile(FILE *stream);
//一个线程可以执行多个flockfile() 调用,而流在程序执行相同数量的funlockfile()调用前不会解锁。
int ftrylockfile(FILE *stream); //flockfile()的非堵塞版本。
当前流加了锁,不做任何处理,返回非零值;没有锁,增加锁计数,成为流的所有者线程,返回零。
不加锁流的操作:
#define _GNU_SOURCE
#include <stdio.h>
int getc_unlocked(FILE *stream);
int getchar_unlocked(void);
int putc_unlocked(int c, FILE *stream);
int putchar_unlocked(int c);
void clearerr_unlocked(FILE *stream);
int feof_unlocked(FILE *stream);
int ferror_unlocked(FILE *stream);
int fileno_unlocked(FILE *stream);
int fflush_unlocked(FILE *stream);
int fgetc_unlocked(FILE *stream);
int fputc_unlocked(int c, FILE *stream);
size_t fread_unlocked(void *ptr, size_t size, size_t n,
FILE *stream);
size_t fwrite_unlocked(const void *ptr, size_t size, size_t n,
FILE *stream);
char *fgets_unlocked(char *s, int n, FILE *stream);
int fputs_unlocked(const char *s, FILE *stream);
#include <wchar.h>
wint_t getwc_unlocked(FILE *stream);
wint_t getwchar_unlocked(void);
wint_t fgetwc_unlocked(FILE *stream);
wint_t fputwc_unlocked(wchar_t wc, FILE *stream);
wint_t putwc_unlocked(wchar_t wc, FILE *stream);
wint_t putwchar_unlocked(wchar_t wc);
wchar_t *fgetws_unlocked(wchar_t *ws, int n, FILE *stream);
int fputws_unlocked(const wchar_t *ws, FILE *stream);
它们不检查或获得指定流上的锁,这些函数与相对的加锁函数执行相同操作。
标准I/O:双副本的性能影响,当读取数据时,read()调用从内核复制数据到标准I/O缓冲区,另一程序执行fgetc()时,数据从缓冲区复制到指定的缓冲区。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了