加入收藏 | 设为首页 | 会员中心 | 我要投稿 宿州站长网 (https://www.0557zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

C 标准库 IO 使用分析

发布时间:2021-12-05 13:07:54 所属栏目:教程 来源:互联网
导读:其实输入与输出对于不管什么系统的设计都是异常重要的,比如设计 C 接口函数,首先要设计好输入参数、输出参数和返回值,接下来才能开始设计具体的实现过程。C 语言标准库提供的接口功能很有限,不像 Python 库。不过想把它用好也不容易,本文总结 C 标准库

 
printf("%dn", 5);            // 打印整数 5
printf("-%10s-n", "hello")   // 设置显示宽度并左对齐:-     hello-
printf("-%-10s-n", "hello")  // 设置显示宽度并右对齐:-     hello-
printf("%#xn", 0xff);        // 0xff 不加#则显示ff
printf("%pn", main);         // 打印 main 函数首地址
printf("%%n");               // 打印一个 %
scanf 就是从标准输入中读取格式化数据,简单举个例子:
 
int year, month, day;
scanf("%d/%d/%d", &year, &month, &day);
printf("year = %d, month = %d, day = %dn", year, month, day);
(2). sprintf / sscanf / snprintf
sprintf 并不打印到文件,而是打印到用户提供的缓冲区中并在末尾加 '',由于格式化后的字符串长度很难预计,所以很可能造成缓冲区溢出,强烈推荐 snprintf 更好一些,参数 size 指定了缓冲区长度,如果格式化后的字符串超过缓冲区长度,snprintf 就把字符串截断到 size - 1 字节,再加上一个 '',保证字符串以 '' 结尾。如果发生截断,返回值是截断之前的长度,通过对比返回值与缓冲区实际长度对比就知道是否发生截断。
 
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("%sn", buf);
// 结果为:123456
取指定长度的字符串
sscanf("123456", "%4s", buf);
printf("%sn", buf);
// 结果为:1234
取第1个字符串
sscanf("hello world", "%s", buf);
printf("%sn", 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-%fn", 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 操作。缓冲区大小也可以自定义设置,一般情况无需设置,默认即可。

(编辑:宿州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章