缓冲输入和输出

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()时,数据从缓冲区复制到指定的缓冲区。

 

posted @   愿得入睡  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示