C|深入理解输入输出流、文件抽象及相关库函数
liebian365 2024-11-21 17:35 3 浏览 0 评论
计算机的核心就在于用数据来模拟事物,数据和处理数据的代码输入并存储到内存,处理后产生输出。
数据的输入可以来源于键盘、扫描仪、磁盘文件、控件,字符串缓存、网络连接设备(文件)。
数据的输的去向可以是显示屏、打印机、磁盘文件、控件,字符串缓存、网络连接设备(文件)。
指令集是对CPU的抽象,文件是对输入/输出设备的抽象,虚拟存储器是对程序存储的抽象,进程是对一个正在运行的程序的抽象,而虚拟机是对整个计算机(包括操作系统、处理器和程序)的抽象。
console程序与图形界面程序的区别在于,前者是通过函数调用,而后者则是通过消息传递,如单击工具栏图标或菜单项或命令按钮。另外的区别在于输入与输出的方式不同,Windows采用控件这种窗口进行交互。
1 输入输出流的概念
C语言的数据输入使用输入流的概念,可以简单理解为流的数据按“先入先出”的方式提取(extraction)或插入(insertion)。
流实际上是程序中输入或者输出设备的一种抽象表示,它表示了信息从源端到目的端的运动。在输入操作时,字节流从输入设备(如键盘、磁盘)流向内存;在输出操作时,字节流从内存流向输出设备(例如屏幕、打印机、磁盘等)。流中的内容可以是ASCII字符、二进制形式的数据、图形图像、数字音频视频或其他形式的信息。
实际上,在内存中为每个数据流开辟一个内存缓冲区,是用来存放流中的数据。流是与内存缓冲区相对应的,或者可以说,缓冲区中的数据就是流。
C的一个基本原则是避免不必要的成分。
在嵌入式编程中,I/O就不是必需的,所以C并没有内置输入输出语句,而是通过库实现。
<stdio.h> 中声明的大多数函数是对一个流进行操作, 这个流和一个打开的文件相关联。在开始执行程序的时候,将自动打开3 个文件和相关的流:
stdin:读取文本的标准源(标准输入流)。
stdout:写入文本的标准目的地(标准输出流)。
stderr:输出错误信息的标准目的地(标准错误流)。
它们都是FILE*型的指针。流提供了文件和程序的通信通道。
<stdio.h> 中声明的许多函数都用到了这些流, 用户不需要对它们命名。
对那些需要一个流参数的函数, 可以选其中一个名字作为流参数。
也可以通过名字打开一个文件, 并和一个流连接起来。可以通过调用函数fopen或freopen把—个流和—个打开的文件关联起来, 例如:
FILE* fptr = fopen (fname, fmode) ;
FILE* fptr = freopen(fname, fmode, fptr);
只有当这两个函数使用模式fmode打开文件名为fname的文件, 并且把该文件和fptr指向的对象控制的流联系起来时, 它们才返回指向FILE类型的指针类型的非空值。
结构体FILE定义了以下数据成员:
文件结束符:记录是否需要结束此文件。
错误指示符:记录读或写是否导致不可恢复的数据传输错误。
文件定位符:记录从文件中读出或写入的下一个字节(这在某些类型的文件中可能没有定义)。
缓冲信息:记录任何读写缓冲区是否存在和它的大小。
状态信息:决定下一个是读操作还是写操作。
demo:
#include <stdio.h>
#include <stdlib.h>
struct Student{
char name[24];
int age;
double chinese;
double math;
};
int main()
{
struct Student arr[100] = {0};
int n,i;
printf("请输入需要输入数据的学生个数:");
scanf("%d",&n);
printf("请输入每个学生的数据:\n");
for(i=0;i<n;i++)
scanf("%s %d %lf %lf",&arr[i].name,&arr[i].age,&arr[i].chinese,&arr[i].math);
printf("数据输出到控制台:\n");
for(i=0;i<n;i++)
printf("%s %d %.1f %.1f\n", arr[i].name,arr[i].age,arr[i].chinese,arr[i].math);
printf("数据输出到磁盘文件,并打开文件。\n");
FILE *fout = fopen("student.txt", "w" );
if(fout != NULL )
for(i=0;i<n;i++)
fprintf(fout, "%s %d %.1f %.1f\n", arr[i].name,arr[i].age,arr[i].chinese,arr[i].math);
fclose(fout);
system("student.txt");
getchar(); // 读取回车字符'\n'
getchar(); // 等待输入,停住console窗口
return 0;
}
/* 输入的数据可以一次性复制到console,复制到数据形成一个流,程序从流中逐个读取数据
3
wwu 18 66 77
fwu 17 99 68
zwu 16 78 96
*/
输入的数据可以一次性复制到console,全部数据保存到了stdin缓冲区,stdin也是一个FILE*指针类型,有文件定位符(文件位置指示器)。输入函数按要求从缓冲区中读取数据。在它读取数据时,文件位置指示器被设置为指向刚读取字符的下一个字符。由于stdio.h系列的所有输入函数都使用相同的缓冲区, 所以调用任何一个函数都将从上一次函数停止调用的位置开始。
2 输入输出流的方向
数据的输入输出按流的概念进行抽象,有方向的概念:
3 参数、数据来源和去向不同的12个scanf()和printf()
以下I/O函数都使用了一个格式字符串const char *format。
格式format由一条或者多条指示组成:由符号%引导的字符与变长参数(变量)列表形成一一对应的关系,后者按前者指示的类型进行解析。
对于输出函数的format,普通的多字节字符(%除外), 它们原封不动地被复制到输出流。format参数是一个指向只读的以空字符结尾的字符串的指针。这个格式告诉函数期望附加的参数是什么(如果有的话)以及如何转换它们。它也指定了散布在任意转换后的参数之间的字面量文本。
对于输入函数的format,格式告诉函数所需要的附加参数(如果有的话)和怎样把输入域转换为要存储的值。它也指定转换域之间想要匹配的字面量文本或空白。如果扫描格式看起来和打印格式非常相似,那么这种相似很有可能是有意的。格式由零个或者多个指示性语句组成:一个或者多个空白字符,一个普通的多字节字符(不是%和空白字符),或者一个转换说明。每一个转换说明都由字符%引入。%*是赋值取消操作(也就是忽略)。一个转换说明符指示通过读取输入来执行, 直到遇到第一个非空白字符(不对它进行读取),或者直到再也没有字符可以读取。一个普通的多字节字符指示读取流的下一个字符。如果一个字符和组成控制语句的字符不同, 则指示失败, 不同的字符和它后面的字符都不能被读取。跳过输入的空白字符(由isspace函数指定),除非说明包含一个l、C或者n说明符。一个输入项是输入字符的最长的匹配序列,除非它超过了指定的字段宽度,这种情况下它是序列中前面的该长度个字符。如果输入项后面还有字符的话,后面的第一个字符仍然没有被读取。如果输入项的长度为零,则指示执行失败:这种情况是匹配失败,除非一个错误阻止从流的输入,这种情况是输入失败。
函数sprintf等价于fprintf,只不过参数s指定了一个数组, 生成的输出写入到这个数组中,而不是写到一个流中。在写入的字符最后写入一个空字符, 它不计入返回和的一部分。如果两个交迭的对象之间发生了复制, 则行为未定义。
函数vfprintf等价千函数fprintf, 只不过可变参数表用arg代替,它已经被宏va_start (可能后来还有va_arg调用)初始化了。函数vfprintf不调用宏va_end。
输入要保持开放,输出要保持保守。(Be liberal in what you accept, and conservative in what you send.)
在程序设计中,大量的错误是发生在输入或输出范围的边界上,而不是发生在输入输出范围的内部。
3.1 printf
#include <stdio.h>
int printf(const char *format, ... );
printf()函数根据format(格式)给出的格式打印输出到stdout(标准输出)。
字符串format(格式)由两类项目组成 - 显示到屏幕上的字符和定义printf()显示的其它参数。 基本上, 你可以指定一个包含文本在内的format(格式)字符串,也可以是映射到printf()其它参数的"特殊"字符。
char name[20] = "Bob";
int age = 21;
printf( "Hello %s, you are %d years old\n", name, age );
显示下列输出:
Hello Bob, you are 21 years old
3.2 scanf
#include <stdio.h>
int scanf(const char *format, ... );
scanf()函数根据由format(格式)指定的格式从stdin(标准输入)读取,并保存数据到其它参数。 它和printf()有点类似。format(格式)字符串由控制字符,空白字符和非空白字符组成。控制字符以一个%符号开始。
#include <stdio.h>
int main ()
{
char str [80];
int i;
printf ("Enter your family name: ");
scanf ("%79s",str);
printf ("Enter your age: ");
scanf ("%d",&i);
printf ("Mr. %s , %d years old.\n",str,i);
printf ("Enter a hexadecimal number: ");
scanf ("%x",&i);
printf ("You have entered %#x (%d).\n",i,i);
return 0;
}
3.3 fprintf
#include <stdio.h>
int fprintf( FILE *stream, const char *format, ... );
fprintf()函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件。
fprintf()的返回值是输出的字符数,发生错误时返回一个负值。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20] = "Mary";
int age = 17;
FILE *out;
out = fopen( "output.txt", "w" );
if( out != NULL )
fprintf( out, "%s is %d old.\n", name,age);
fclose(out);
system("output.txt");
return 0;
}
fprintf()和printf()一样工作,前者数据的去向是由stream(流)指定的文件,后者是stdout。
printf("hello world\n");
fprintf(stdout,"hello world\n");
3.4 fscanf
#include <stdio.h>
int fscanf( FILE *stream, const char *format, ... );
函数fscanf()以scanf()的执行方式从给出的文件流中读取数据。
fscanf()的返回值是事实上已赋值的变量的数,如果未进行任何分配时返回EOF。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20] = "";
int age = 0;
FILE *fin;
fin = fopen( "output.txt", "r" );
if(fin != NULL )
fscanf(fin, "%s is %d old.\n", &name,&age);
fclose(fin);
printf("%s %d\n",name,age);
system("output.txt");
return 0;
}
fscanf()数据的来源是stream(流)指定的文件,后者是stdin。
int a;
scanf("%d",&a);
fscanf(stdin,"%d",&a);
3.5 sprintf
#include <stdio.h>
int sprintf( char *buffer, const char *format, ... );
sprintf()函数和printf()类似,只是把输出发送到buffer(缓冲区)中。
返回值是写入的字符数量。
char string[50];
int file_number = 0;
sprintf( string, "file.%d", file_number );
file_number++;
output_file = fopen( string, "w" );
3.6 sscanf
#include <stdio.h>
int sscanf( const char *buffer, const char *format, ... );
函数sscanf()和scanf()类似,只是输入从buffer(缓冲区)中读取。
#include <stdio.h>
int main ()
{
char sentence []="Rudolph is 12 years old";
char str [20];
int i;
sscanf (sentence,"%s %*s %d",str,&i);
printf ("%s -> %d\n",str,i); // Rudolph -> 12
getchar();
return 0;
}
3.7 vprintf, vfprintf和 vsprintf
#include <stdarg.h>
#include <stdio.h>
int vprintf( char *format, va_list arg_ptr );
int vfprintf( FILE *stream, const char *format, va_list arg_ptr );
int vsprintf( char *buffer, char *format, va_list arg_ptr );
这些函数和printf()非常相似,fprintf()和 sprintf()的不同在于参数列表是一个指向一系列参数的指针。 va_list在STDARG.H中定义,并且也可以被va_arg()使用。
void error( char *fmt, ... ) {
va_list args;
va_start( args, fmt );
fprintf( stderr, "Error: " );
vfprintf( stderr, fmt, args );
fprintf( stderr, "\n" );
va_end( args );
exit( 1 );
}
ref:
http://www.cplusplus.com/reference/cstdio/
https://www.shouce.ren/api/c/index.htm#
P.J. Plauger 《The standard C library》
-End-
相关推荐
- 快递查询教程,批量查询物流,一键管理快递
-
作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...
- 一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递
-
对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...
- 快递查询单号查询,怎么查物流到哪了
-
输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...
- 3分钟查询物流,教你一键批量查询全部物流信息
-
很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...
- 快递单号查询,一次性查询全部物流信息
-
现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...
- 快递查询工具,批量查询多个快递快递单号的物流状态、签收时间
-
最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...
- 快递查询软件,自动识别查询快递单号查询方法
-
当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...
- 教你怎样查询快递查询单号并保存物流信息
-
商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...
- 简单几步骤查询所有快递物流信息
-
在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...
- 物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号
-
最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...
- 连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息
-
快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...
- 快递查询教程,快递单号查询,筛选更新量为1的单号
-
最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...
- 掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析
-
在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...
- 从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息
-
在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...
- 物流单号查询,在哪里查询快递
-
如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)