Linux系统编程_4_标准I/O(附:清空缓冲区方法)

标准I/O属于库文件,系统调用和库是有区别的,为了方便,标准库中实现了和所有系统调用同名的函数;参考《APUE》

这里部分不解释过多,网上的资料很多,其实熟悉的人基本都知道,我们不可能记住所有的函数的,特别是参数等等,我们能做的就是尽量熟悉他,用到时查一下就能用就行了。

标准I/O函数,摘自于网络:

当打开一个流时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构,它包含了标准I/O库为管理该流所需的所有信息,包括:用于实际I/O的文件描述符、指向用于该缓冲区的指针、缓冲区的长度、当前在缓冲区的字符数以及出错标志等。为引用一个流,需将FILE指针作为参数传递给每个标准I/O函数。

对于标准输入、标准输出和标准出错,他们的文件描述符对应STFIN_FILENO、STDOUT_FILENO和STDERR_FILENO。这三个标准I/O流通过预定义stdin、stdout和stderr加以引用。这三个文件指针以及标准I/O函数都定义在头文件<stdio.h>中。

缓冲

标准I/O库提供缓冲的目的是尽可能减少使用read和write调用次数。提供了三种类型的缓冲:

1) 全缓冲:需在填满标准I/O缓冲区后才进行实际I/O操作。

2) 行缓冲:当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。

3) 不带缓冲:标准I/O库不对字符进行缓冲存储。

一般而言,标准出错是不带缓冲的,打开终端设备的流是行缓冲的,其他所有流则是全缓冲的。当流是全缓冲,但该缓冲区是局部填写时,可用fflush函数冲洗。

可调用下面的函数更改缓冲区类型:

#include <stdio.h>

void setbuf(FILE *stream, char *buf);

int setvbuf(FILE *stream, char *buf, intmode, size_t size);

任何时候,我们都可以强制冲洗一个流:

#include <stdio.h>

int fflush(FILE *stream);

此函数将使该流所有未写的数据都被传送至内核。作为一个特例,如若fp是NULL,则此函数将导致所有输出流被冲洗。

打开流

#include <stdio.h>

FILE *fopen(const char *path, const char*mode);

FILE *fdopen(int fd, const char *mode);

FILE *freopen(const char *path, const char*mode, FILE *stream);

这三个函数的区别是:

1) fopen打开一个指定的文件。

2) fropen在一个指定的留上打开一个指定的文件,如若该流已经打开,则先关闭该流。如若该流已经定向,则fopen清除该定向。此函数一般用于将一直指定的文件打开为一个预定义的流:标准输入、标准输出或标准错误。

3) fdopen获取一个现有的文件描述符,并使一个标准的I/O流与该描述符相结合。此函数常用于由创建管道和网络通信函数返回的描述符。因为这些特殊类型的文件不能用标准I/Ofopen函数打开,所有我们必须先调用设备专用函数以获得一个文件描述符,然后用fopen使一个标准I/O与该描述符相关联。

其中的mode参数可以用是以下15种不同的值:

r或rb: 为读打开

w或wb: 把文件截短至0长,或为写而创建

a或ab: 添加;为在文件写打开,或为写打开

r+或r+b或rb+: 为读和写打开

w+或w+b或wb+: 把文件截短至0,或为读和写打开

a+或a+b或ab+: 为在文件尾端读和写而打开或创建

#include <stdio.h>

int fclose(FILE *fp);

在文件被关闭之前,冲洗缓冲区中的输出数据。如果标准I/O库已经为该流自动分配了一个缓冲区,则释放缓冲区。

读和写流

一旦打开了流,则可在三种不同类型的非格式化I/O中进行选择,对其读、写操作:

1) 每次一个字符是I/O。一次读或写一个字符,如果流是带缓冲区的,则标准I/O函数会处理所有缓冲。

2) 每次一行的I/O。如果想要一次读或写一行,则使用fgets和fputs。每行都以一个换行符终止。当调用fgets时,应说明能处理的最大行长。

3) 直接I/O。fread和fwrite函数支持这种类型的I/O。

每次一个字符I/O

输入函数:

#include <stdio.h>

int getc(FILE *stream);

int fgetc(FILE *stream);

int getchar(void);

getchar()等价于getc(stdin)。getc和fgetc区别在于getc可被实现为宏,而fgetc则不能实现为宏。

不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分出错和到达文件尾端,必须调用ferror和feof函数。

#include <stdio.h>

int feof(FILE *stream);

int ferror(FILE *stream);

这两个函数的返回值:若条件为真则返回非0值,否则返回0。

每个流在FILE对象中维持了两个标志:出错标志和文件结束标志

调用clearerr则清除这两个标志。

void clearerr(FILE *stream);

从流读取数据后,可以调用ungetc将字符再压入回流中。

int ungetc(int c, FILE *stream);

压入回流中的字符以后又可以从流中读出,但读出的字符顺序与压送回的顺序相反。

对于输出函数:

#include <stdio.h>

int putc(int c, FILE *stream);

int fputc(int c, FILE *stream);

int putchar(int c);

与输入函数一样putchar(c)等效于putc(c, stdout)。putc可实现为宏。

每次一行I/O

#include <stdio.h>

char *fgets(char *s, int size, FILE*stream);

char *gets(char *s);

fgets从指定的流读,必须指定缓冲区长度size。此函数一直读到下一个换行符为止,但是不超过n-1个字符,读入的字符被送入缓冲区。该缓冲区以null字符结尾。如若改行(包括最后一个换行符)的字符数超过n-1,则fgets只返回一个不完整的行,但是缓冲区总是以null字符结尾。对fgets的下一次调用会继续读改行。

gets从标准输入读。它是一个不推荐的函数,因为不能指定缓冲区长度,可能造成缓冲区溢出,写到缓冲区之后的存储空间中,从而产生不可预料的后果。

fputs和puts提供每次输出一行的功能。

int fputs(const char *s, FILE *stream);

int puts(const char *s);

二进制I/O

#include <stdio.h>

size_t fread(void *ptr, size_t size,size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_tsize, size_t nmemb, FILE *stream);

以上两个函数可一次读或写整个结构。

定位流

有三种方法定位标准I/O流

1) ftell和fseek。这两个函数要求文件的位置可以存放到一个长整形中。

2) ftello和fseeko。他们可以使文件文件偏移量不一定使用长整形。他们用off_t数据类型代替了长整形。

3) fgetpos和fsetpos。他们使用抽象数据类型fpos_t记录文件的位置。这种数据累心可以定义为记录一个文件的位置所需的长度。

#include <stdio.h>

int fseek(FILE *stream, long offset, intwhence);

long ftell(FILE *stream);

int fseeko(FILE *stream, off_t offset, intwhence);

off_t ftello(FILE *stream);

int fgetpos(FILE *stream, fpos_t *pos);

int fsetpos(FILE *stream, fpos_t *pos);

格式化I/O

输出:

#include <stdio.h>

int printf(const char *format, ...);

int fprintf(FILE *stream, const char*format, ...);

int sprintf(char *str, const char *format,...);

int snprintf(char *str, size_t size, constchar *format, ...);

输入:

#include <stdio.h>

int scanf(const char *format, ...);

int fscanf(FILE *stream, const char*format, ...);

int sscanf(const char *str, const char*format, ...);

fileno函数

标准I/O库最终都要调用I/O系统调用函数。每个标准I/O流都有一个与其相关联的文件描述符,可以对一个流调用fileno函数以获得其描述符。

int fileno(FILE *stream);

临时文件

#include <stdio.h>

char *tmpnam(char *s);

FILE *tmpfile(void);

tmpnam函数产生一个与现有文件名不同的一个有效路径名字字符串。每次调用它,它都产生一个不同的路径名,最多调用TMP_MAX次。

tmpfile创建一个临时二进制文件。

此外还有两个类似的函数:

#include <stdio.h>

char *tempnam(const char *dir, const char*pfx);

int mkstemp(char *template);

下面只给出一个简单的小例子:

实例,实现文件的拷贝:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void fun()
{
    printf("End!\n");
}

int main(int argc, char **argv)
{
    FILE *pSrc = NULL;
    FILE *pDst = NULL;
    int c;

    if(argc < 3)
    {
        printf("Usage: ./cp file dstFile!\n");
        exit(-1);
    }
    if((pSrc = fopen(argv[1], "r")) == NULL)
    {perror("Open file failure!");
        exit(-1);
    }
    if((pDst = fopen(argv[2], "w")) == NULL)
    {
        perror("Create dstFile failure!");
        exit(-1);
    }

    while( ( c = fgetc(pSrc) ) != EOF )
    {
        fputc(c, pDst);
    }

    fclose(pSrc);
    fclose(pDst);

    atexit(fun);

    return 0;
}                                                                                                                                                         <pre name="code" class="cpp">/*
int main(int argc, char **argv)
{
    char c;
    int fd;
    FILE * fp;

    fprintf(stdout, "%d\n%c\n", 100, 'F');

    fp = fopen("io.c", "r");
    if(NULL == fp)
    {
        printf("Open io.c failure!\n");
    }

    fd = fileno(fp);
    printf("Open io.c Success!, The fp=%p, fd=%d\n", fp, fd);
    printf("The current process id is:%d\n", getpid());

    while((c=getc(stdin)) != EOF)
    {
        putc(c, stdout);
    }

    return 0;
}
*/<span style="font-family: Arial, Helvetica, sans-serif;">                                                                                                                                                                         </span>

小记一下:

Linux中清空缓冲区方法:

有时使用多次scanf时,可能会出现前面scanf的内容仍保留在输入缓冲区中,这样就会出现错误。

在Linux中,用getchar();fflush(stdin);rewind(stdin)等,都不能达到预期的效果,原因是他们是windows环境下使用的。

Linux中要清空缓冲区,可以使用:setbuf(stdin, NULL);

时间: 2024-10-18 15:04:19

Linux系统编程_4_标准I/O(附:清空缓冲区方法)的相关文章

嵌入式 Linux系统编程(三)——标准IO库

嵌入式 Linux系统编程(三)--标准IO库 与文件IO函数相类似,标准IO库中提供的是fopen.fclose.fread.fwrite等面向流对象的IO函数,这些函数在实现时本身就要调用linux的文件IO这些系统调用. 一.标准IO库函数的缓冲机制 由于IO设备的访问速度与CPU的速度相差好几个数量级,为了协调IO设备与CPU的速度的不匹配,对于块设备,内核使用了页高速缓存,即数据会先被拷贝到操作系统内核的页缓存区中,然后才会从操作系统内核的缓存区拷贝到应用程序的地址空间. 当应用程序尝

Linux系统编程@进程通信(一)

进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统的一个分支) POSIX进程间通信(POSIX:可移植操作系统接口,为了提高UNIX环境下应用程序的可移植性.很多其他系统也支持POSIX标准(如:DEC OpenVMS和Windows).) 现在Linux使用的进程间通信方式包括: 管道(pipe).有名管道(FIFO) 信号(signal) 消

Linux系统编程之访问文件夹及其文件属性

1. 文件夹操作:opendir, readdir, closedir 2. 文件属性:lstat 3. 实现功能:获取指定文件夹下所有的文件(使用递归),因此就能计算所有文件大小之类的啦... 代码示例如下: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/stat

嵌入式 Linux系统编程(一)——文件IO

嵌入式 Linux系统编程(一)--文件IO 一.文件IO概念 linux文件IO操作有两套大类的操作方式:不带缓存的文件IO操作,带缓存的文件IO操作.不带缓存的属于直接调用系统调用(system call)的方式,高效完成文件输入输出.它以文件标识符(整型)作为文件唯一性的判断依据.这种操作不是ASCI标准的,与系统有关,移植有一定的问题.而带缓存的是在不带缓存的基础之上封装了一层,维护了一个输入输出缓冲区,使之能跨OS,成为ASCI标准,称为标准IO库.不带缓存的方式频繁进行用户态 和内核

嵌入式 Linux系统编程(四)——文件属性

嵌入式 Linux系统编程(四)--文件属性 一.文件属性概述 Linux 文件的属性主要包括:文件的节点.种类.权限模式.链接数量.所归属的用户和用户组.最近访问或修改的时间等内容.文件属性示例如下: 多个文件属性查看: ls -lih 1341714 -rw-r--r-- 1 root root 2.5K May 28 10:24 bit_marco.c 1341718 -rw-r--r-- 1 root root 2.1K May 28 09:08 bit_marco.c~ 1341706

嵌入式 Linux系统编程(五)——目录文件函数

嵌入式 Linux系统编程(五)--目录文件函数 Linux中目录也是文件,目录操作函数为标准IO库函数.主要函数如下: #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); DIR *fdopendir(int fd); 成功返回一个指向目录流的指针,失败返回NULL,并且设置errno全局变量. #include <dirent.h> struct dirent *rea

嵌入式 Linux系统编程(六)——系统信息

嵌入式 Linux系统编程(六)--系统信息 一.时间 Linux系统下常用的时间类型:time_t.struct tm.struct timeval.struct timespec. 1.time_t类型时间 time_t实际是一个长整型.其值表示为从UTC(coordinated universal time)时间1970年1月1日00时00分00秒(也称为Linux系统的Epoch时间)到当前时刻的秒数.由于time_t类型长度的限制,它所表示的时间不能晚于2038年1月19日03时14分

Linux系统编程【转】

转自:https://blog.csdn.net/majiakun1/article/details/8558308 一.Linux系统编程概论 1.1 系统编程基石 syscall: libc:标准C库.系统调用封装.线程库.基本应用工具 gcc: 1.2 模块接口 API:应用程序编程接口,源代码级别,能通过编译,由标准C语言定义,libc来实现 ABI:应用程序二进制接口,二进制级别,能正常运行,关注调用约定.字节序.寄存器使用.系统调用.链接.二进制格式等,很难实现 1.3 错误处理 <

LINUX系统编程 由REDIS的持久化机制联想到的子进程退出的相关问题

19:22:01 2014-08-27 引言: 以前对wait waitpid 以及exit这几个函数只是大致上了解,但是看REDIS的AOF和RDB 2种持久化时 均要处理子进程运行完成退出和父进程需要做的什么事情,所以特定看了UNIX环境编程和LINUX系统编程这2本书 重新梳理下整个要点. 内容: 一般而言: 如果程序类似于下面的情况: if((pid=fork())==0) { dochildtthing(); exit(0); } else if(pid>0) { dofathertt