文件
一、格式化的输入输出
printf
%[flags][width][.prec][hlL]type
1.flag
flag | 含义 |
---|
- | 左对齐 |
+ | 在前面放+或- |
(space) | 正数留空 |
0 | 0填充 |
#include<stdio.h>
int main(int argc, char const *argv[])
{
printf("%9d\n",123);
printf("% d\n",123);
printf("% d\n",-123);
printf("%09d\n",123);
printf("%-9d\n",123);
printf("%+9d",123);
return 0;
}
输出:
123
123
-123
000000123
123
+123
2.width或.prec
width或.prec | 含义 |
---|
number | 最小字符数 |
* | 下一个参数是字符数 |
.number | 小数点后的位数 |
.* | 下一个参数是小数点后的位数 |
#include<stdio.h>
int main(int argc, char const *argv[])
{
printf("%9.2f\n",123.0);
printf("%*d\n",9,123);
printf("%9.*f\n",2,123.0);
return 0;
}
输出:
123.00
123
123.00
3.hlL
类型修饰 | 含义 |
---|
hh | 单个字节 |
h | short |
l | long |
ll | long long |
L | long double |
4.type
type | 用于 | type | 用于 |
---|
i或d | int | g | float |
u | unsigned int | G | float |
o | 八进制 | a或A | 十六进制浮点 |
x | 十六进制 | c | char |
X | 字母大写的十六进制 | s | 字符串 |
f或F | float,6 | p | 指针 |
e或E | 指数 | n | 读入/写出的个数 |
scanf
1.flag
flag | 含义 | flag | 含义 |
---|
* | 跳过 | l | long,double |
数字 | 最大字符数 | ll | long long |
hh | char | L | long double |
h | short | | |
#include<stdio.h>
int main(int argc, char const *argv[])
{
int num;
scanf("%d*%d",&num);
printf("%d",num);
return 0;
}
输入:
123 456
输出:
123
2.type
type | 用于 | type | 用于 |
---|
d | int | s | 字符串(单词) |
i | 整数,可能为十六进制或八进制 | […] | 所允许的字符 |
u | unsigned int | p | 指针 |
o | 八进制 | | |
x | 十六进制 | | |
a,e,f,g | float | | |
c | char | | |
#include<stdio.h>
int main(int argc, char const *argv[])
{
int num;
scanf("%i",&num);
printf("%d",num);
return 0;
}
输入:
123
输出:
123
输入:
0x12
输出:
18
输入:
012
输出:
10
printf
和scanf
的返回值
- 读入的项目数
- 输出的字符数
- 在要求严格的程序中,应该判断每次调用
scanf
或printf
的返回值,从而了解程序运行中是否存在问题
#include<stdio.h>
int main(int argc, char const *argv[])
{
int a,num = 123;
a = printf("%d",num);
printf("\n");
printf("%d",a);
return 0;
}
输出:
123
3
二、文件输入输出
用>
和<
做重定向
#include<stdio.h>
int main(int argc, char const *argv[])
{
int num;
scanf("%d",&num);
printf("%d",num);
return 0;
}
注:上述文件位于D:\Developer\C_code\try文件夹内,名为demo.c
在终端中执行:
D:\Developer\C_code\try>gcc demo.c -o demo.exe
D:\Developer\C_code\try>demo.exe
123456
123456
D:\Developer\C_code\try>demo.exe > demo.txt
123456
D:\Developer\C_code\try>type demo.txt
123456
D:\Developer\C_code\try>demo.exe < demo.txt
123456
D:\Developer\C_code\try>
注:上述操作均在windows平台的cmd内执行,powershell内无法使用符号<
FILE
- FILE* fopen(const char * restrict path, const char *restrict mode);
- int fclose(FILE *stream);
- fscanf(FILE*,…)
- fprintf(FILE*,…)
FILE* fp = fopen("file","r");
if (fp)
{
fscanf(fp,...);
fclose(fp);
}
else
{
...
}
参数 | 效果 |
---|
r | 打开只读 |
r+ | 打开读写,从文件头开始 |
w | 打开只写。如果不存在则新建,如果存在则清空 |
w+ | 打开读写。如果不存在则新建,如果存在则清空 |
a | 打开追加。如果不存在则新建,如果存在则从文件尾开始 |
在上述参数后加x | 只新建,如果文件已存在则不能打开 |
demo.txt内容为:
123456
#include<stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("demo.txt","r");
if (fp)
{
int num;
fscanf(fp, "%d",&num);
printf("%d\n",num);
fclose(fp);
}
else
{
printf("无法打开文件\n");
}
return 0;
}
输出:
123456
三、二进制文件
二进制文件
- 其实所有的文件最终都是二进制的
- 文本文件无非是用最简单的方式可以读写的文件
- 而二进制文件是需要专门的程序来读写的文件
- 文本文件的输入输出是格式化,可能经过转码
文本VS二进制
- Unix喜欢用文本文件来做数据存储和程序配置
- 交互式终端的出现使得人们喜欢用文本和计算机"talk”
- Unix的shell提供了一些读写文本的小程序
- Windows喜欢用二进制文件
- DOS是草根文化,并不继承和熟悉Unix文化
- PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
- 文本的优势是方便人类读写,而且跨平台
- 文本的缺点是程序输入输出要经过格式化,开销大
- 二进制的缺点是人类读写困难,而且不跨平台
- 二进制的优点是程序读写快
程序为什么要文件
- 配置
- 数据
- 媒体
- 现实是,程序通过第三方库来读写文件,很少直接读
写二进制文件了
二进制读写
size_t fread(void *restrict ptr, size_t size, size_tnitems, FILE*restrict stream);
size t fwrite(const void *restrict ptr, size_t size,size_t nitems, FILE*restrict stream);
- 注意FILE指针是最后一个参数
- 返回的是成功读写的字节数
为什么nitem
?
- 因为二进制文件的读写一般都是通过对一个结构变量
的操作来进行的 - 于是
nitem
就是用来说明这次读写几个结构变量!
写入实例:
#include<stdio.h>
#define STR_LEN 20
typedef struct _student {
char name[STR_LEN];
int gender;
int age;
} Student;
void getList(Student aStu[],int number);
int save(Student aStu[],int number);
int main(int argc, char const *argv[])
{
int number = 0;
printf("请输入学生数量:");
scanf("%d",&number);
Student aStu[number];
getList(aStu,number);
if (save(aStu,number))
{
printf("保存成功\n");
}
else
{
printf("保存失败\n");
}
return 0;
}
void getList(Student aStu[],int number)
{
char fomat[STR_LEN];
sprintf(fomat,"%%%ds", STR_LEN-1);
int i;
for ( i = 0; i < number; i++)
{
printf("第%d个学生:\n",i);
printf("\t姓名:");
scanf(fomat, aStu[i].name);
printf("\t性别(0-男,1-女,2-其他):");
scanf("%d",&aStu[i].gender);
printf("\t年龄:");
scanf("%d",&aStu[i].age);
}
}
int save(Student aStu[],int number)
{
int ret = -1;
FILE *fp = fopen("stdent.data","w");
if (fp)
{
ret = fwrite(aStu,sizeof(Student),number,fp);
fclose(fp);
}
return ret == number;
}
执行结果:
-----------------------------------
请输入学生数量:3
第0个学生:
姓名:张三
性别(0-男,1-女,2-其他):0
年龄:18
第1个学生:
姓名:李四
性别(0-男,1-女,2-其他):1
年龄:19
第2个学生:
姓名:王五
性别(0-男,1-女,2-其他):2
年龄:18
保存成功
在文件中定位
long ftell(FILE*stream);
int fseek(FILE*stream, long offset,int whence);
SEEK_SET
:从头开始SEEK_CUR
:从当前位置开始SEEK_END
:从尾开始(倒过来)
读取实例
#include<stdio.h>
#define STR_LEN 20
typedef struct _student {
char name[STR_LEN];
int gender;
int age;
} Student;
void read(FILE *fp,int index);
int main(int argc, char const *argv[])
{
FILE *fp = fopen("student.data","r");
if (fp)
{
fseek(fp,0L,SEEK_END);
long size = ftell(fp);
int number = size / sizeof(Student);
int index = 0;
printf("有%d个数据,你要看第几个:",number);
scanf("%d",&index);
read(fp, index-1);
fclose(fp);
}
return 0;
}
void read(FILE *fp,int index)
{
fseek(fp,index*sizeof(Student),SEEK_SET);
Student stu;
if (fread(&stu,sizeof(Student),1,fp)==1)
{
printf("第%d个学生:",index+1);
printf("\t姓名:%s\n",stu.name);
printf("\t性别:");
switch (stu.gender)
{
case 0:printf("男\n");break;
case 1:printf("女\n");break;
case 2:printf("其他\n");break;
}
printf("\t年龄:%d\n",stu.age);
}
}
执行结果:
-------------------------------
有3个数据,你要看第几个:3
第3个学生: 姓名:王五
性别:其他
年龄:18
可移植性
- 这样的二进制文件不具有可移植性
- 在
int
为32位的机器上写成的数据文件无法直接在int
为64位的机器上正确读出 - 解决方案之一是放弃使用
int
,而是typedef
具有明确大小的类型 - 更好的方案是用文本