Buffered I/O read & write

2019-08-13  本文已影响0人  无无吴

User-Buffered I/O

出于性能考虑,内核通过延迟写入、合并相邻的I/O请求和提前读取来在内部缓冲数据。通过不同的方式,用户缓冲也旨在改进性能。

Block Size

实际上,块的大小通常是512、1024、2048、4096或8192字节。
使用stat()系统调用或stat(1)命令可以很容易地计算出给定设备的块大小。
然而,通常不需要知道实际的块大小
最简单的选择是使用大缓冲区大小执行I/O,这是典型块大小的公共倍数。4096字节和8192字节都工作得很好。
但问题是程序很少用块来处理。程序处理字段、行和单个字符,而不是抽象如块。
User-Buffered I/O缩小了文件系统(以块形式)与应用程序(在其自身抽象)进行对话之间的差距。
可以在您自己的程序中手动实现用户缓冲。事实上,许多关键任务应用程序就是这样做的。然而,绝大多数程序都是standard I/O库(标准C库的一部分)或iostream库(标准C++库的一部分),它提供了一个健壮和有能力的用户缓冲解决方案。

Standard I/O

标准C库提供了标准的I/O库(通常简单地称为stdio)。

File Pointers

标准I/O例程不直接对文件描述符进行操作。相反,他们使用自己的唯一标识符,称为文件指针。在C库中,文件指针映射到文件描述符。文件指针由一个指向FILE的指针表示,该文件在<stdio.h>中定义。

Opening Files

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

Modes

mode参数描述如何打开给定文件。它是以下字符串之一:

FILE *stream;
stream = fopen("/etc/manifest", "r");
if(!stream)
      /*error*/

Opening a Stream via File Descriptor

#include <stdio.h>
FILE *fdopen(int fd, const char* mode);

mode与fopen()相同,并且必须与最初用于打开文件描述符的模式兼容。
模式w和w+是特殊的,它们不会导致截断。流定位在与文件描述符相关联的文件位置。一旦文件描述符转换为 steam,I/O不应再直接在文件描述符上执行。然而,这样做是合法的。请注意,文件描述符没有被复制,仅关联了一个新的stream。 关闭流也将关闭文件描述符。
在成功时,fdopen()返回一个有效的文件指针;如果失败,则返回NULL并适当地设置errno。

int testfdopen() {
    FILE *stream;
    int fd;

    fd = open("../FileIO/testfile.txt", O_RDWR);
    if(fd == -1){
        perror("open");
        return -1;
    }
    stream = fdopen(fd, "w+");
    if(!stream){
        perror("fdopen");
        return -2;
    }

    fclose(stream);
    return 0;
}

int main()
{
    testfdopen();
    return 0;
}
果然没有截断

Closing Stream

#include <stdio.h>
int fclose(FILE *stream);

首先刷新任何缓冲且尚未写入的数据。成功后,fclose()返回0。失败时,它返回EOF并相应地设置错误号。

Closing All Streams

函数关闭与当前进程关联的所有流,包括标准输入、标准输出和标准错误:

#define _GNU_SOURCE
#include <stdio.h>
int fcloseall(void);

在关闭之前,所有的流都会被冲洗。函数总是返回0;它是特定于Linux的。

Reading from a Stream

Reading a Character at a Time

#include <stdio.h>
int  fgetc(FILE *stream);
int c;
c = fgetc(stream)
if(c == EOF)
    /* error */
else
    printf("c=%c\n", (char)c);

Putting the character back

#include <stdio.h>
int ungetc(int c, FILE *stream);
int testungetc() {
    FILE *stream = fopen("test.txt", "r");
    if(!stream){
        perror("fopen");
        return -1;
    }
    int ret;
    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = ungetc(ret, stream);
    if(ret == EOF){
        perror("ungetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);

    ret = fgetc(stream);
    if(ret == EOF){
        perror("fgetc");
        return -1;
    }
    printf("ret = %c\n", (char)ret);


    fclose(stream);

    return 0;
}

int main()
{
    testungetc();
    return 0;
}
测试结果

Reading an Entire Line

#include <stdio.h>
char * fgets(char *str, int size, FILE *stream);
char buf[LINE_MAX];
if(!fgets(buf, LINE_MAX, stram))
  /*error*/

POSIX defines LINE_MAX in <limits.h>

int testfgets() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[LINE_MAX] = {0};

    if(!fgets(buf, LINE_MAX, stream)){
        perror("fgets - 1");
        return -1;
    }
    printf("1 - %s\n", buf);

    if(!fgets(buf, LINE_MAX, stream)){
        perror("fgets - 2");
        return -1;
    }
    printf("2 - %s\n", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfgets();
    return 0;
}
测试结果
int testfgets() {
    FILE *stream = fopen("aaaa.txt", "r");
    while(1) {
        char buf[LINE_MAX] = {0};
        if (!fgets(buf, LINE_MAX, stream)) {
            perror("fgets - 1");
            break;
        }
        printf("%s", buf);
    }
    return 0;
}

int main()
{
    testfgets();
    return 0;
}
测试结果

上述测试表明:读取的最后一个字节后,将一个空字符(\0)存储在缓冲区中。因此printf打印室不用再加上\n,不然会多一个空行。

Reading arbitrary strings

char *s;
int c = 0; s = str;
while (--n > 0 && (c = fgetc (stream)) != EOF && (*s++ = c) != d) ;
if (c == d)
    *--s = '\0';
else
    *s = '\0';

将d设置为\n将提供类似于fget()的行为,减去在缓冲区中存储换行符。

Reading Binary Data

对于某些应用程序,读取单个字符或行是不够的。有时,开发人员希望读写复杂的二进制数据,例如C结构体。为此,standard I/O提供fread():

#include <stdio.h>
size_t fread(void *buf, size_t size, size_t nr, FILE *stream);

对fread()的调用将读取nr个数据的元素,每个大小size字节。
读取的元素数(而不是读取的字节数)会return。
该函数通过小于nr的返回值指示故障或EOF。不幸的是,如果不使用ferror()和feof(),就不可能知道这两种情况中的哪一种。

char buf[64];
size_t nr;
nr = fread (buf, 1, sizeof(buf), stream);
if (nr == 0)
    /* error */
int testfread() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[64] = {0};
    size_t nr;
    nr = fread(buf, 1, sizeof(buf), stream);
    printf("nr: %d\n", nr);
    printf("%s", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfread();
    return 0;
}
测试结果1
int testfread() {
    FILE *stream = fopen("aaaa.txt", "r");
    char buf[64] = {0};
    size_t nr;
    nr = fread(buf, 3, sizeof(buf), stream);
    printf("nr: %d\n", nr);
    printf("%s", buf);
    fclose(stream);
    return 0;
}

int main()
{
    testfread();
    return 0;
}
测试结果2

从上面两个测试可以发现 改变size的大小,返回的nr也会对应改变,这个size就是指每次读取的自己长度。

Writing to a Stream

Writing a Single Character

#include <stdio.h>
int fputc (int c, FILE *stream);

成功完成后,函数返回c。否则,返回EOF,并适当地设置errno。

if (fputc ('p', stream) == EOF)
  /* error */

Writing s String of Characters

#include <stdio.h>
int fputs (const char *str, FILE *stream);

在成功时,fpust()返回一个非负数。如果失败,则返回EOF。

FILE *stream;
stream = fopen ("journal.txt", "a");
if (!stream)
    /* error */
if (fputs ("The ship is made of wood.\n", stream) == EOF)
    /* error */
if (fclose (stream) == EOF)
    /* error */

Writing Binary Data

#include <stdio.h>
size_t fwrite (void *buf, size_t size, size_t nr, FILE *stream);

Buffered I/0 样例

int testBufferedIO() {
    FILE *in, *out;
    struct pirate{
        char name[100];
        unsigned long booty;
        unsigned long beard_len;
    }p, blackbeard = {"Edward Teach", 950, 48};

    out = fopen("data", "w");
    if(!out){
        perror("fopen");
        return 1;
    }
    if(!fwrite(&blackbeard,
            1, sizeof(blackbeard), 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, 1, sizeof(struct pirate), in)) {
        perror ("fread");
        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;
    
}

int main()
{
    testBufferedIO();
    return 0;
}
测试结果
上一篇 下一篇

猜你喜欢

热点阅读