栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

9.标准I/O库

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

9.标准I/O库

        标准 I/O 和文件 I/O 都是 C 语言函数,但是标准 I/O 是标准 C 库函数,而文件 I/O 则是 Linux
系统调用;
⚫ 标准 I/O 是由文件 I/O 封装而来,标准 I/O 内部实际上是调用文件 I/O 来完成实际操作的;
⚫ 可移植性:标准 I/O 相比于文件 I/O 具有更好的可移植性,通常对于不同的操作系统,其内核向应用层提供的系统调用往往都是不同,譬如系统调用的定义、功能、参数列表、返回值等往往都是不一样的;而对于标准 I/O 来说,由于很多操作系统都实现了标准 I/O 库,标准 I/O 库在不同的操作系统之间其接口定义几乎是一样的,所以标准 I/O 在不同操作系统之间相比于文件 I/O 具有更好的可移植性。
⚫ 性能、效率: 标准 I/O 库在用户空间维护了自己的 stdio 缓冲区, 所以标准 I/O 是带有缓存的,而文件 I/O 在用户空间是不带有缓存的,所以在性能、效率上,标准 I/O 要优于文件 I/O。

一、FILE指针

        使用标准 I/O 库函数打开或创建一个文件时,会返回一个指向 FILE 类型对象的指针(FILE *) ,使用该 FILE 指针与被打开或创建的文件相关联,然后该 FILE 指针就用于后续的标准 I/O 操作。因此FILE指针相当于文件I/O中的文件描述符fd。

二、标准输入、标准输出和标准错误

        用户通过标准输入设备与系统进行交互, 进程将从据(譬如程序中 printf 打印输出的字符串) 输出到标报错打印的信息)输出到标准错误(stderr) 文件。

        每个进程启动之后都会默认打开标准输入、标准输出以及标准错误, 得到三个文件描述符, 即 0、 1、2, 其中 0 代表标准输入、 1 代表标准输出、 2 代表标准错误; 在应用编程中可以使用宏 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 分别代表 0、 1、 2,这些宏定义在 unistd.h 头文件中:

#define STDIN_FILENO 0 
#define STDOUT_FILENO 1 
#define STDERR_FILENO 2 

        而在标准I/O中没有文件描述符的概念,因此对文件进行读写等操作需要依托于FILE类型指针进行,在 stdio.h 头文件中有相应的定义,如下:

extern struct _IO_FILE *stdin; 
extern struct _IO_FILE *stdout; 
extern struct _IO_FILE *stderr; 

#define stdin stdin
#define stdout stdout
#define stderr stderr

所以,在标准 I/O 中,可以使用 stdin、 stdout、 stderr 来表示标准输入、标准输出和标准错误。


三、 标准I/O函数

        fopen()打开文件

        在文件I/O中通过使用open函数打开或创建文件,而在标准I/O中可以使用库函数fopen打开或创建文件。fopen()函数原型如下所示:

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

使用该函数需要包含头文件 stdio.h。
函数参数和返回值含义如下:
path: 参数 path 指向文件路径,可以是绝对路径、也可以是相对路径。
mode: 参数 mode 指定了对该文件的读写权限,是一个字符串,稍后介绍。
返回值: 调用成功返回一个指向 FILE 类型对象的指针(FILE *),该指针与打开或创建的文件相关联,
后续的标准 I/O 操作将围绕 FILE 指针进行。 如果失败则返回 NULL,并设置 errno 以指示错误原因。

参数 mode 字符串类型,可取值为如下值之一:

mode说明对应于 open()函数的 flags 参数取值
r以只读方式打开文件。O_RDONLY
r+以可读、可写方式打开文件。O_RDWR
w以只写方式打开文件,如果参数 path 指定的文件
存在,将文件长度截断为 0;如果指定文件不存在
则创建该文件
 

O_WRONLY | O_CREAT | O_TRUNC


 
w+以可读、可写方式打开文件,如果参数 path 指定
的文件存在,将文件长度截断为 0;如果指定文件
不存在则创建该文件。
O_RDWR | O_CREAT | O_TRUNC
 
a以只写方式打开文件,打开以进行追加内容(在
文件末尾写入),如果文件不存在则创建该文
件
 
O_WRONLY | O_CREAT | O_APPEND
 
a+以可读、可写方式打开文件,以追加方式写入
(在文件末尾写入),如果文件不存在则创建该
文件。
 
O_RDWR | O_CREAT | O_APPEND
 

        调用 fopen()函数新建文件时无法手动指定文件的权限,但却有一个默认值:

S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH (0666)

使用示例:
        使用只读方式打开文件:

fopen(path, "r");

        使用可读、可写方式打开文件:

fopen(path, "r+");

        使用只写方式打开文件,并将文件长度截断为 0,如果文件不存在则创建该文件:

fopen(path, "w");

fclose()关闭文件

#include 
int fclose(FILE *stream);
参数 stream 为 FILE 类型指针,调用成功返回 0;失败将返回 EOF(也就是-1),并且会设置 errno 来
指示错误原因

fwrite()和fread() 

#include 
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

库函数 fread()用于读取文件数据,其参数和返回值含义如下:
ptr: fread()将读取到的数据存放在参数 ptr 指向的缓冲区中;
size: fread()从文件读取 nmemb 个数据项,每一个数据项的大小为 size 个字节,所以总共读取的数据大
小为 nmemb * size 个字节。
nmemb: 参数 nmemb 指定了读取数据项的个数。
stream: FILE 指针。
返回值: 调用成功时返回读取到的数据项的数目(数据项数目并不等于实际读取的字节数,除非参数
size 等于 1);如果发生错误或到达文件末尾,则 fread()返回的值将小于参数 nmemb,那么到底发生了错误
还是到达了文件末尾, fread()不能区分文件结尾和错误, 究竟是哪一种情况,此时可以使用 ferror()或 feof()


库函数 fwrite()用于将数据写入到文件中,其参数和返回值含义如下:
ptr: 将参数 ptr 指向的缓冲区中的数据写入到文件中。
size: 参数 size 指定了每个数据项的字节大小,与 fread()函数的 size 参数意义相同。
nmemb: 参数 nmemb 指定了写入的数据项个数,与 fread()函数的 nmemb 参数意义相同。
stream: FILE 指针。
返回值: 调用成功时返回写入的数据项的数目(数据项数目并不等于实际写的字节数,除非参数
size 等于 1);如果发生错误,则 fwrite()返回的值将小于参数 nmemb(或者等于 0)。

        使用案例如下:

  fwrite.c

        使用 fopen()函数将当前目录下的 test_file 文件打开,调用 fopen()时 mode 参数设置为"w",表示以只写的方式打开文件,并将文件的长度截断为 0,如果指定文件不存在则创建该文件。打开文件之后调用fwrite()函数将"Hello World!"字符串数据写入到文件中

#include 
#include 

int main(void)
{
    char buf[] = "Hello World!n";
    FILE *fp = NULL;
    
    if (NULL == (fp = fopen("./test_file", "w"))) {
        perror("fopen error");
        exit(-1);
    }

    printf("文件打开成功!n");


    
    if (sizeof(buf) >
    fwrite(buf, 1, sizeof(buf), fp)) {
        printf("fwrite errorn");
        fclose(fp);
        exit(-1);
    }
    printf("数据写入成功!n");
    
    fclose(fp);
    exit(0);
}

fread.c

        使用 fread()函数从文件中读取 12 * 1=12 个字节的数据,将读取到的数据存放在 buf 中,当读取到的字节数小于指定字节数时,表示发生了错误或者已经到达了文件末尾,程序中调用了库函数 ferror()来判断是不是发生了错误。

#include 
#include 


int main(void)
{
    char buf[50] = {0};
    FILE *fp = NULL;
    int size;
    
    if ((fp = fopen("./test_file", "r")) == NULL) {
        perror("fopen error");
        exit(-1);
    }
    printf("文件打开成功!n");

    
    if ((size = fread(buf, 1, 12, fp)) < 12) {
    if (ferror(fp)) { //使用 ferror 判断是否是发生错误
        printf("fread errorn");
        fclose(fp);
        exit(-1);
    }

    
    }
    printf("成功读取%d 个字节数据: %sn", size, buf);
    
    fclose(fp);
    exit(0);
}

编译后运行结果如下:

fseek函数

库函数 fseek()的作用类似于系统调用 lseek(), 用于设置文件读写位置偏移量, lseek()用于文件 I/O,而库函数 fseek()则用于标准 I/O,其函数原型如下所示:

#include 
int fseek(FILE *stream, long offset, int whence);

函数参数和返回值含义如下:
stream: FILE 指针。
offset: 与 lseek()函数的 offset 参数意义相同。
whence: 与 lseek()函数的 whence 参数意义相同。
返回值: 成功返回 0;发生错误将返回-1,并且会设置 errno 以指示错误原因; 与 lseek()函数的返回值
意义不同,这里要注意!

ftell()函数

库函数 ftell()可用于获取文件当前的读写位置偏移量,其函数原型如下所示:

#include 
long ftell(FILE *stream);
参数 stream 指向对应的文件,函数调用成功将返回当前读写位置偏移量;调用失败将返回-1,并会设置
errno 以指示错误原因

feof()函数
        feof()用于测试参数 stream 所指文件的 end-of-file 标志,如果 end-of-file 标志被设置了,则调用feof()函数将返回一个非零值,如果 end-of-file 标志没有被设置,则返回 0。

#include 
int feof(FILE *stream);
当文件的读写位置移动到了文件末尾时, end-of-file 标志将会被设置。

用法如下:
if (feof(file)) {

}
else {

}

ferror()函数

库函数 ferror()用于测试参数 stream 所指文件的错误标志,如果错误标志被设置了,则调用 ferror()函数将返回一个非零值,如果错误标志没有被设置,则返回 0。

#include 
int ferror(FILE *stream);

当对文件的 I/O 操作发生错误时,错误标志将会被设置。
if (ferror(file)) {

}
else {

}

clearerr()函数

库函数 clearerr()用于清除 end-of-file 标志和错误标志,当调用 feof()或 ferror()校验这些标志后,通常需要清除这些标志,避免下次校验时使用到的是上一次设置的值,此时可以手动调用 clearerr()函数清除标志。

#include 
void clearerr(FILE *stream);

格式化输出: 

        C 库函数提供了 5 个格式化输出函数,包括: printf()、 fprintf()、 dprintf()、 sprintf()、 snprintf(),其函数定义如下所示:

#include 
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t size, const char *format, ...);

printf函数 

        作为最常用的函数可以实现,将程序的字符串信息输出到终端。常用的用法如下:

输出Hello Linux

printf("Hello Linuxn");

输出数字5 

printf("%dn",5);

fprintf函数 

        fprintf()可将格式化数据写入到由 FILE 指针指定的文件中,譬如将字符串写入到标准输出:

#include 

int main(void)
{
    fprintf(stdout, "Hello worldn");
    fprintf(stdout, "数字是%dn",5);

    return 0;
}

运行结果如下:

dprintf()函数

        dprintf()可将格式化数据写入到由文件描述符 fd 指定的文件中,譬如将字符串“Hello World”写入到标准输出:

#include 
#include 

int main(void)
{
    dprintf(STDOUT_FILENO, "Hello World!n");
    dprintf(STDOUT_FILENO, "数字是%dn",6);

    return 0;
}

运行结果如下:

sprintf()函数
        sprintf()函数将格式化数据存储在由参数 buf 所指定的缓冲区中, 譬如将字符串“Hello World”存放在缓冲区中:

#include 
#include 

int main(void)
{
    char buf[20];   //长度为20的缓冲区
    sprintf(buf, "Hello World!");
    printf("%sn", buf);
    return 0;
}

运行结果如下:

        sprintf()函数可能会造成由参数 buf 指定的缓冲区溢出,调用者有责任确保该缓冲区足够大,因为缓冲区溢出会造成程序不稳定甚至安全隐患!

snprintf()函数

        snprintf()函数;在该函数中,使用参数 size 显式的指定缓冲区的大小,如果写入到缓冲区的字节数大于参数 size 指定的大小,超出的部分将会被丢弃!

格式化输入:

C 库函数提供了 3 个格式化输入函数,包括: scanf()、 fscanf()、 sscanf(),其函数定义如下所示:

#include 
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

scanf()函数

int a, b, c;
scanf("%d %d %d", &a, &b, &c);

fscanf()函数
        fscanf()函数从指定文件中读取数据,作为格式转换的输入数据,文件通过 FILE 指针指定,所以它有两个固定参数, FILE 指针和格式控制字符串 format。譬如从标准输入文件中读取数据进行格式化转换:

int a, b, c;
fscanf(stdin, "%d %d %d", &a, &b, &c);

sscanf()函数

        sscanf()将从参数 str 所指向的字符串缓冲区中读取数据,作为格式转换的输入数据,所以它也有两个固定参数,字符串 str 和格式控制字符串 format,譬如

char *str = "5454 hello";
char buf[10];
int a;
sscanf(str, "%d %s", &a, buf);
文件描述符与 FILE 指针互转

        库函数 fileno()可以将标准 I/O 中使用的 FILE 指针转换为文件 I/O 中所使用的文件描述符,而 fdopen()则进行着相反的操作,其函数原型如下所示:

#include 
int fileno(FILE *stream);
FILE *fdopen(int fd, const char *mode);

fdopen()函数与 fileno()功能相反,给定一个文件描述符,得到该文件对应的 FILE 指针,之后便可以使
用诸如 fread()、 fwrite()等标准 I/O 方式操作文件了。参数 mode 与 fopen()函数中的 mode 参数含义相同

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983348.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号