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,一经查实,立即删除!

相关文章

python调用系统_python 执行系统命令

Python中执行系统命令常见的几种方法:(1)os.system# 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息# 如果再命令行下执行,结果直接打印出来例如:>>> import os>>> os.system(ls)chk_err_log.py…

linux libbz2.so.1,libbz2.so.1.0: cannot open shared object file: No such file or directory

在解决这个报错的过程中,我学到了Linux下解决问题的一种新思路。这个报错很早之前就遇到过,当时是用自己安装的miniconda来下载安装samtools(非root用户),安装完成之后运行就这样报错了,但是其他的软件又可以正常运行,…

python文件对象是可以迭代的_详解python可迭代对象、迭代器和生成器

可迭代对象什么是可迭代对象?顾名思义就是可以迭代的一个对象,再通俗点就是可以被for循环遍历的对象,如常用的list、str等数据类型。我们可以使用isinstance来判断这个数据是否是可迭代对象,在此要先从Iterable包中导入模块collec…

linux更改nginx最大访问数,Linux下nginx服务的配置文件nginx.conf中模块的讲解之配置limit_conn_zone来限制并发连接数以及下载速率...

一、限制并发连接数的配置方法如下:1、在nginx.conf里的http{}里加上如下代码:# vim /usr/local/nginx/conf/nginx.conf#在其中的36行加入下面的内容(在http{}模块中加入的)limit_conn_zone $binary_remote_addr zoneaddr:10m;#解释内容如下:要限制连接&…

python实现杨辉三角形博客园_Python实现杨辉三角

杨辉三角,是二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形。帕斯卡(1623----1662)是在1654年发现这一规律的,比杨辉要迟393年,比贾宪迟600年。杨辉三角是中国古代数学的杰出研究成果之一,它把二…

ftm模块linux驱动,飞思卡尔k系列_ftm模块详解.doc

飞思卡尔k系列_ftm模块详解1.5FTM模块1.5.1 FTM模块简介FTM模块是一个多功能定时器模块,主要功能有,PWM输出、输入捕捉、输出比较、定时中断、脉冲加减计数、脉冲周期脉宽测量。在K10中,共有FTM0,FTM1,FTM2三个独立的F…

python列表切片图解_Python列表切片操作实例总结

本文实例讲述了Python列表切片操作。分享给大家供大家参考,具体如下:切片指的是列表的一部分。1 基本用法指定第一个元素和最后一个元素的索引,即可创建切片 。Python 会在到达指定的第二个索引之前元素后停止 。books[半生缘,往事并不如烟,心…

乱码 讯飞 语音识别_一段讯飞、百度等语音识别API无法识别的语音最终解决办法...

最近在做语音识别、字幕扒词相关的工作,遇到了一段录音(https://download.csdn.net/download/u014220286/12169183,各位有兴趣的可以下载下来试试),音质什么的和其他处理过的无二异,也是普通话,照常理说应该能识别出来…

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

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

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

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

用c语言编写名字用字母表示,如何用C语言编写自己的姓名和学号

可以参考下面的代码&#xff1a;#include<stdio.h>#include<stdlib.h>intmain(void){FILE*fpfopen("data.txt","r");if(!fp){fpfopen("data.txt","w");printf("请输入姓名:");char*name;scanf("%s",n…

天大c语言离线考核答案,【天大考核】2019年秋学期考试《公共关系学》离线作业考核试题答案100分...

【天大考核】答案100分奥鹏在线离线作业wx&#xff1a;zcjiaoyu1|QQ&#xff1a;2502533676|论文2019年秋学期考试《公共关系学》离线作业考核试题公共关系学要求&#xff1a;一、 独立完成&#xff0c;下面已将五组题目列出&#xff0c;请任选其中一组题目作答&#xff0c;每人…

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

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

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

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

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

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

厦门大学2016年c语言程序设计,厦门大学c语言程序设计2016模拟题讲评及课程复习.pptx...

《C语言程序设计》课程复习及模拟题讲评;《C语言程序设计》试卷各类题目分数分布情况一、选择题 40分(20小题, 每题2分 ) 主要考核基本概念, 词法, 语法和小程序阅读等。二、阅读题 12分(2小题, 每题6分)三、改错题 12分(2小题, 每题6分)四、设计题 36分(4小题, 每题9分) ;启动…

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

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

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

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

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

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

pytorch 对抗样本_《AI安全之对抗样本入门》—3.4 PyTorch

3.4 PyTorchPyTorch是torch的Python版本&#xff0c;是由Facebook开源的神经网络框架。PyTorch虽然是深度学习框架中的后起之秀&#xff0c;但是发展极其迅猛。PyTorch提供了NumPy风格的Tensor操作&#xff0c;熟悉NumPy操作的用户非常容易上手。我们以解决经典的手写数字识别…