linux下c标准库位置,C 标准库 IO 使用详解

其实输入与输出对于不管什么系统的设计都是异常重要的,比如设计 C 接口函数,首先要设计好输入参数、输出参数和返回值,接下来才能开始设计具体的实现过程。C 语言标准库提供的接口功能很有限,不像 Python 库。不过想把它用好也不容易,本文总结 C 标准库基础 IO 的常见操作和一些特别需要注意的问题,如果你觉着自己还不是大神,那么请相信我,读完全文后你肯定会有不少收获。

一、操作句柄

打开文件其实就是在操作系统中分配一些资源用于保存该文件的状态信息及文件的标识,以后用户程序可以用这个标识做各种读写操作,关闭文件则释放占用的资源。

打开文件的函数:

#include

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

FILE 是 C 标准库定义的结构体类型,其包含文件在内核中的标识(文件描述符)、I/O 缓冲区和当前读写位置信息,调用者不需知道 FILE 的具体成员,由库函数内部维护,调用者不应该直接访问这些成员。像 FILE* 这样的文件指针称为句柄(Handle)。

打开文件操作是对文件资源进行操作的,所以有可能打开文件失败,所以在打开函数时一定要判断返回值,如果失败则返回错误信息,以方便快速定位错误。

打开文件应该与关闭文件成对存在,虽然程序在退出时会释放相应的资源,但是对于一个长时间运行服务程序来说,经常打开而不关闭文件是会造成进程资源耗尽的,因为进程的文件描述符个数是有限的,及时关闭文件是个好习惯。

关闭文件的函数:

#include

int fclose(FILE *fp);

fopen 函数参数 mode 总结:

"r":只读,文件必须存在。

"w":只写,如果不存在则创建,存在则覆盖。

"a":追加,如果不存在则创建。

"r+":允许读和写,文件必须存在。

"w+":允许读和写,文件不存在则创建,存在则覆盖。

"a+":允许读和追加,文件不存在则创建。

二、关于stdin/stdout/stderr

在用户程序启动时,main 函数还没开始执行之前,会自动打开三个 FILE* 指针分别是:stdin、stdout、stderr,这三个文件指针是 libc 中定义的全局变量,在 stdio.h 中声明,printf 向 stdout 写,而 scanf 从 stdin 读,用户程序也可以直接使用这三个文件指针。

stdin 只用于读操作,称为标准输入

stdout 只用于写操作,称为标准输出

stderr 也用于写操作,称为标准错误输出

通常程序的运行结果打印到标准输出,而错误提示打印到标准错误输出,一般标准输出和标准错误都是屏幕。通常可以标准输出重定向到一个常规文件,而标准错误输出仍然对应终端设备,这样就可以将运行结果与错误信息分开。

三、以字节为单位的IO函数

fgetc 函数从指定的文件中读一个字节,getchar从标准输入读一个字节,调用 getchar() 相当于 fgetc(stdin)

#include

int fgetc(FILE *stream);

int getchar(void);

fputc 函数向指定的文件写入一个字节,putchar 向标准输出写一个字节,调用 putchar() 相当于调用 fputc(c, stdout)。

#include

int fputc(int c, FILE *stream);

int putchar(int c);

参数和返回值类型为什么使用 int 类型?可以看到这几个函数的参数和返回值类型都是 int,而非 unsigned char 型。因为错误或读到文件末尾时将返回 EOF,即 -1,如果返回值是 unsigned char(0xff),与实际读到字节 0xff 无法区分,如果使用 int 就可以避免这个问题。

四、操作读写位置函数

当我们在操作文件时,有一个叫「文件指针」的家伙来记录当前操作的文件位置,比如刚打开文件,调用了 1 次 fgetc 后,此时文件指针指向了第 1 个字节后边,注意是以字节为单位记录的。

改变文件指针位置的函数:

#include

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

whence:从何处开始移动,取值:SEEK_SET | SEEK_CUR | SEEK_END

offset:移动偏移量,取值:可取正 | 负

void rewind(FILE *stream);

举几个简单例子:

fseek(fp, 5, SEEK_SET); // 从文件头向后移动5个字节

fseek(fp, 6, SEEK_CUR); // 从当前位置向后移动6个字节

fseek(fp, -3, SEEK_END); // 从文件尾向前移动3个字节

offset 可正可负,负值表示向文件开头的方向移动,正值表示向文件尾方向移动,如果向前移动的字节数超过文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入会增加文件尺寸,文件空洞字节都是 0

$ echo "5678" > file.txt

fp = fopen("file.txt", "r+");

fseek(fp, 10, SEEK_SET);

fputc('K', fp)

fclose(fp)

// 通过结果可以看出字母K是从第10个位置开始写的

liwei:/tmp$ od -tx1 -tc -Ax file.txt

0000000 35 36 37 38 0a 00 00 00 00 00 4b

5 6 7 8 \n \0 \0 \0 \0 \0 K

rewind(fp) 等价于 fseek(fp, 0, SEEK_SET)

ftell(fp) 函数比较简单,直接返回当前文件指针在文件中的位置

// 实现计算文件字节数的功能

fseek(fp, 0, SEEK_END);

ftell(fp);

五、以字符串为单位的IO函数

fgets 从指定的文件中读一行字符到调用者提供的缓冲区,读入内容不超过 size 。

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

char *gets(char *s);

首先要说明 gets() 函数强烈不推荐使用,类似 strcpy 函数,用户不可以指定缓冲区大小,很容易造成缓冲区溢出错误。不过 strcpy 程序员还是可以避免,而 gets 的输入用户可以提供任意长的字符串,唯一避免方法就是不使用 gets,而使用 fgets(buf, size, stdin)

fgets 函数从 stream 所指文件读取以 '\n' 结尾的一行,包括 '\n' 在内,存到缓冲区中,并在该行结尾添加一个 '\0' 组成完整的字符串。如果文件一行太长,fgets 从文件中读了 size-1 个字符还没有读到 '\n',就把已经读到的 size-1 个字符和一个 '\0' 字符存入缓冲区,文件行剩余的内容可以在下次调用 fgets 时继续读。

若一次 fgets 调用在读入若干字符后到达文件末尾,则将已读到的字符加上 '\0' 存入缓冲区并返回,如果再次调用则返回 NULL,可以据此判断是否读到文件末尾。

fputs 向指定文件写入一个字符串,缓冲区保存的是以 '\0' 结尾的字符串,与 fgets 不同的是,fputs 不关心字符串中的 '\n' 字符。

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

int puts(const char *s);

六、以记录为单位的IO函数

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 和 fwrite 用于读写记录,这里的记录是指一串固定长度的字节,比如一个 int、一个结构体货或一个定长数组。

参数 size 指出一条记录的长度,nmemb 指出要读或写多少条记录,这些记录在 ptr 所指内存空间连续存放,共占 size * nmemb 个字节。

fread 和 fwrite 返回的记录数有可能小于 nmemb 指定的记录数。例如当读写位置距文件末尾只有一条记录长度,调用 fread 指定 nmemb 为 2,则返回值为 1。如果写文件时出错,则 fwrite 的返回值小于 nmemb 指定的值。

struct t{

int a;

short b;

};

struct t val = {1, 2};

FILE *fp = fopen("file.txt", "w");

fwrite(&val, sizeof(val), 1, fp);

fclose(fp);

liwei:/tmp$ od -tx1 -tc -Ax file.txt

0000000 01 00 00 00 02 00 00 00

001 \0 \0 \0 002 \0 \0 \0

从结果可以看出,写入的是 8 个字节,有兴趣的同学可以就此分析下系统的「大小端」和结构体的「对齐补齐」问题。

七、格式化IO函数

(1). printf / scanf

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

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

这两个函数是我们学习 C 语言最早接触,可能也是接触比较多的了,没什么特别要说的。printf 就是格式化打印到标准输出。下面总结下 printf 常用的方式。

printf("%d\n", 5); // 打印整数 5

printf("-%10s-\n", "hello") // 设置显示宽度并左对齐:- hello-

printf("-%-10s-\n", "hello") // 设置显示宽度并右对齐:- hello-

printf("%#x\n", 0xff); // 0xff 不加#则显示ff

printf("%p\n", main); // 打印 main 函数首地址

printf("%%\n"); // 打印一个 %

scanf 就是从标准输入中读取格式化数据,简单举个例子:

int year, month, day;

scanf("%d/%d/%d", &year, &month, &day);

printf("year = %d, month = %d, day = %d\n", year, month, day);

(2). sprintf / sscanf / snprintf

sprintf 并不打印到文件,而是打印到用户提供的缓冲区中并在末尾加 '\0',由于格式化后的字符串长度很难预计,所以很可能造成缓冲区溢出,强烈推荐 snprintf 更好一些,参数 size 指定了缓冲区长度,如果格式化后的字符串超过缓冲区长度,snprintf 就把字符串截断到 size - 1 字节,再加上一个 '\0',保证字符串以 '\0' 结尾。如果发生截断,返回值是截断之前的长度,通过对比返回值与缓冲区实际长度对比就知道是否发生截断。

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

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

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

sscanf 是从输入字符串中按照指定的格式去读取相应的数据,函数功能非常的强大,支持类似正则表达式匹配的功能。具体的使用格式请自行查询官方手册,这里总结出最常用、最重要的几种使用场景和方式。

最基本的用法

char buf[1024] = 0;

sscanf("123456", "%s", buf);

printf("%s\n", buf);

// 结果为:123456

取指定长度的字符串

sscanf("123456", "%4s", buf);

printf("%s\n", buf);

// 结果为:1234

取第1个字符串

sscanf("hello world", "%s", buf);

printf("%s\n", buf);

// 结果为:hello 因为默认是以空格来分割字符串的,%s读取第一个字符串hello

读取到指定字符为止的字符串

sscanf("123456#abcdef", "%[^#]", buf);

// 结果为:123456

// %[^#]表示读取到#符号停止,不包括#

读取仅包含指定字符集的字符串

sscanf("123456abcdefBCDEF", "%[1-9a-z]", buf);

// 结果为:123456abcdef

// 表达式是要匹配数字和小写字母,匹配到大写字母就停止匹配了。

读取指定字符集为止的字符串

sscanf("123456abcdefBCDEF", "%[^A-Z]", buf);

// 结果为:123456abcdef

读取两个符号之间的内容(@和.之间的内容)

sscanf("liwei0526vip@linuxblogs.cn", "%*[^@]@%[^.]", buf);

// 结果为:linuxblogs

// 先读取@符号前边内容并丢弃,然后读@,接着读取.符号之前的内容linuxblogs,不包含字符.

给一个字符串

sscanf("hello, world", "%*s%s", buf);

// 结果为:world

// 先忽略一个字符串"hello,",遇到空格直接跳过,匹配%s,保存 world 到 buf

// %*s 表示第 1 个匹配到的被过滤掉,即跳过"hello,",如果没有空格,则结果为 NULL

稍微复杂点的

sscanf("ABCabcAB=", "%*[A-Z]%*[a-z]%[^a-z=]", buf);

// 结果为:AB 自己尝试分析哈

包含特殊字符处理

sscanf("201*1b_-cdZA&", "%[0-9|_|--|a-z|A-Z|&|*]", buf);

// 结果为:201*1b_-cdZA&

如果能将上述几个例子搞明白,相信基本上已经掌握了 sscanf 的用法,实践才是检验真理的唯一标准,只有多使用,多思考才能真正理解它的用法。

(3). fprintf / fscanf

fprintf 打印到指定的文件 stream 中,fscanf 从文件中格式化读取数据,类似 scanf 函数。相关函数的声明如下:

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

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

还是通过简单实例来说明基本用法。

FILE *fp = fopen("file.txt", "w");

fprintf(fp, "%d-%s-%f\n", 32, "hello", 0.12);

fclose(fp);

liwei:/tmp$ cat file.txt

32-hello-0.120000

而 fscanf 函数的使用基本上与 sscanf 函数使用方式相同。

八、IO缓冲区

还有个关于 IO 非常重要的概念,就是 IO 缓冲区。

C 标准库为每个打开的文件分配一个 I/O 缓冲区,用户调用读写函数大多数都在 I/O 缓冲区中读写,只有少数请求传递给内核。

以 fgetc/fputc 为例,当第一次调用 fgetc 读一个字节时,fgetc 函数可能通过系统调用进入内核读 1k 字节到缓冲区,然后返回缓冲区中第一个字节给用户,以后用户再调用 fgetc,就直接从缓冲区读取。

另一方面,fputc 通常只是写到缓冲区中,如果缓冲区满了,fputc 就通过系统调用把缓冲区数据传递给内核,内核将数据写回磁盘。如果希望把缓冲区数据立即写入磁盘,可以调用 fflush 函数。

C 标准库 IO 缓冲区有三种类型:全缓冲、行缓冲和无缓冲区,不同类型的缓冲区具有不同的特性。

全缓冲:如果缓冲区写满了就写回内核。常规文件通常是全缓冲的。

行缓冲:如果程序写的数据中有换行符就把这一行写回内核,或者缓冲区满就写回内核。标准输入和标准输出对应终端设备时通常是行缓冲的。

无缓冲:用户程序每次调用库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的,用户程序的错误信息可以尽快输出到设备。

printf("hello world");

while(1);

// 运行程序会发现屏幕并没有打印hello world

// 因为缓冲区没满,且没有\n符号

除了写满缓冲区、写入换行符之外,行缓冲还有一种情况会自动做 flush 操作,如果:

用户程序调用库函数从无缓冲的文件中读取

或从行缓冲的文件中读取,且这次读操作会引发系统调用从内核读取数据,那么会读之前自动 flush 所有行缓冲

程序退出时通常也会自动 flush 缓冲区

如果不想完全依赖自动的 flush 操作,可以调用 fflush 函数手动操作。若调用 fflush(NULL) 可以对所有打开文件的 IO 缓冲区做 flush 操作。缓冲区大小也可以自定义设置,一般情况无需设置,默认即可。

0b1331709591d260c1c78e86d0c51c18.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux docker查看容器状态,Docker容器状态命令行工具——Ctop

Ctop是和Linux top展示效果类似的一个容器状态监视工具,Ctop可以动态的显示容器的cpu、内存、网络的使用情况。一共有两个叫Ctop的命令行工具,分别由GO和Python实现。Python实现的版本功能更强大一些。GO实现版本安装Linux$ wget https://github.com/bci…

plsql表设置主键_对复制实施主键约束

作者:Pedro Gomes 译:徐轶韬在本文中,我们介绍一个配置选项,该选项控制复制通道是否允许创建没有主键的表。这延续了我们最近在复制安全性方面的工作,在该工作中,我们允许用户强制执行权限检查和/或强制执行…

一键刷入twrp_小米/红米手机到手了该怎么解锁和刷 twrp

资源准备:1.百度搜索小米解锁申请,进行申请解锁并下载解锁工具。如图。文件夹里有对应的驱动,要安装好。2.对应机型的 twrp。在 w大的微博下找(wzsx150)或者在酷安找或者去twrp官网。3.edxp相关的包(两个)(不需要框架的可以忽略)4.手(第一步&…

如何将计算思维融合到C语言程序设计中,浅析基于计算思维的C语言程序设计教学...

浅析基于计算思维的C语言程序设计教学摘要:C语言是关键词:计算思维;C语言;教学改革中图分类号:TP3 文献标识码:A文章编号:1009-3044(2020)16-0145-02C语言是计算机相关专业的必修基础课程,是学生接触的第一…

一直在构建工作空间_智能工作空间让Dropbox拥有无限扩展潜力

智能工作空间让Dropbox拥有无限扩展潜力Dropbox一直以“让工作变得更好”的使命。在竞争激烈的市场中,Dropbox有着卓越的历史,就连苹果创始人史蒂夫乔布斯曾经提出来要收购它。Dropbox的智能工作空间是一个开放的生态系统,由于其开放集成的特…

黄金分割小数点后100位小数的c语言编程,黄金分割数小数点后100位

满意答案su304_3212013.03.25采纳率:57% 等级:12已帮助:10017人黄金分割奇妙之处,在于其比例与其倒数是一样的。例如:1.618的倒数是0.618,而1.618:1与1:0.618是一样的。确切值为根号51/2黄金分割数是无理…

去超市一定要存包吗_去东京一定要去的富士河口湖

第一次去河口湖是一个人~也没有留宿,但当时就想说一定要来这边住两天泡温泉,因为实在太!美!了!因为下面这个预告所以就订了一栋小木屋,这是闺蜜先去日本前线发回的~立马改了酒店日期…

分析启动耗时 android,Android app启动耗时分析

首先编译你的程序,打开Android Studio里面的Android Monitor,找到下图的按钮<img src"//bbsmax.ikafan.com/static/L3Byb3h5L2h0dHBzL3BpYzIuemhpbWcuY29tL3YyLTA4Zjk1ZmUxMjM5ODgwNTkzMDU5YjE3YzFlMGU5NjcxX2IucG5…

android+内存清理+代码,最新版本:Android一键式清理,内存清理功能的实现

Android一键式清理,内存清理功能的实山清理大师等均提供一键式清理和一键加速等功能。实际上,它们杀死了一些后台进程以达到释放内存的目的。基本思想是列出所有正在运行的进程,检查它们的重要值(RunningAppProcessInfo.importance&#xff0…

cups共享linux打印机_linux入门-映射网络驱动器

linux入门-映射网络驱动器在日常中,我们不会时时刻刻远程着linux服务器,那么有没有办法可以让我们在window电脑上映射linux的磁盘呢?这是可以实现的,这里我们就要介绍samba了。sambaSamba是在Linux和UNIX系统上实现SMB协议的一个免…

html如何将设置文本效果,css如何对文本进行修饰

color属性:设置文本文字颜色。用法如下:color:颜色值;color属性可以设置的合法颜色值包括:16进制颜色值(例:#ffffff),rgb颜色值【例:rgb(0,0,0)】,rgba颜色值【例:rgb(0,…

HTML与cgi post传递与接收,CGI实例--表单GET与POST示例

CGI概述CGI(Common Gateway Interface: 公用网关接口)规定了Web服务器调用其他可执行程序(CGI程 序)的接口协议标准。Web服务器通过调用CGI程序实现和Web浏览器的交互, 也就是CGI程序通过读标准输入,接受Web浏览器发送给Web服务器的信息, 进行处理, 将响应结果再通过…

html鼠标滑轮换图片,JavaScript实现鼠标滚轮控制页面图片切换

鼠标上的滚轮是一个不错的东东,为什么这么说,因为它能帮助我们快速的浏览网页,快速的进行长篇文章的阅读。对于web前端的我们来说又怎么能不注重这个鼠标滚轮呢,那么它能如何让用户更好的浏览网页呢?本文主要介绍JavaScript实现鼠…

电脑视频html5全屏掉帧,Windows 10使用自带的电影和电视全屏看视频时掉帧(画面卡顿)...

Windows 10自带的“电影和电视”应用能应付一些常见的视频格式,还能播放360度全景视频,对部分人来说用它就够了,不需要额外安装其它播放视频的应用。在使用电影和电视全屏播放视频的过程中,部分人可能会出现掉帧情况,画…

微型计算机的alu部件是包含在,微型计算机的ALU部件是什么?

计算机中执行各种算术和逻辑运算操作的部件。运算器的基本操作包括加、减、乘、除四则运算,与、或、非、异或等逻辑操作,以及移位、比较和传送等操作,亦称算术逻辑部件(ALU)。计算机运行时,运算器的操作和操作种类由控制器决定。运…

工具系列:TensorFlow决策森林_(5)使用文本和神经网络特征

文章目录 设置使用原始文本作为特征使用预训练的文本嵌入同时训练决策树和神经网络构建模型训练和评估模型 欢迎来到 TensorFlow决策森林( TF-DF)的 中级教程。 在本文中,您将学习有关 TF-DF的一些更高级的功能,包括如何处理自…

超级计算机游戏电脑,Salad邀请PC玩家参与全球最大分布式超级计算机的构建

(来自:Salad 官网)据悉,自 2018 年成立以来,Salad 已经在 25 万名 PC 玩家的帮助下,利用闲置的硬件算力、以及开源的桌面应用程序,来帮助验证区块链交易。作为奖励,Salad 用户能够分享计算资源,…

更换锁定计算机图片,电脑锁屏图片怎么设置

电脑锁屏图片怎么设置觉得电脑锁屏的图片单调没有新意?其实大家想知道电脑锁屏图片应该怎么设置吗?下面是小编推荐给大家的电脑锁屏图片怎么设置,希望大家有所收获。同时按下窗口键winR,调出运行对话框,如下图所示运行…

六年级计算机应用计划,2017六年级信息技术下册教学计划

2017六年级信息技术下册教学计划制订教学计划必须按学生的特点制订,不能仿制照搬的计划,只有自己去试着做,摸索出自己的完整方法,才是最有用的。下面应届毕业生考试网小编为大家提供了2017六年级信息技术下册教学计划,…

计算机网络与通信思维导图,用思维导图描述5G场景

随着全球首个5G火车站在上海虹桥火车站启动建设,5G时代离我们越来越近。去年底,工业和信息化部向三大运营商发送了5G系统中低频段试验频率使用许可,5G设备将开始试商用。5G毕竟是新技术,小编今天用思维导图给大家讲解一下5G场景&a…