c语言文件学习笔记

文件

一、格式化的输入输出

printf

  • %[flags][width][.prec][hlL]type
1.flag
flag含义
-左对齐
+在前面放+或-
(space)正数留空
00填充
//格式化输出之flag
#include<stdio.h>
int main(int argc, char const *argv[])
{
    printf("%9d\n",123);//占据9个字节并靠右对齐
    printf("% d\n",123);//正数留空
    printf("% d\n",-123);//负数不变
    printf("%09d\n",123);//占据9个字节并靠右对齐并用0填充
    printf("%-9d\n",123);//占据9个字节并靠左对齐
    printf("%+9d",123);//占据9个字节并强制带正负号


    return 0;
}

输出:
      123
 123
-123
000000123
123
     +123
2.width或.prec
width或.prec含义
number最小字符数
*下一个参数是字符数
.number小数点后的位数
.*下一个参数是小数点后的位数
//格式化输出之width或.prec
#include<stdio.h>
int main(int argc, char const *argv[])
{
    printf("%9.2f\n",123.0);//总共占据9个字节,2位小数,靠右对齐
    printf("%*d\n",9,123);//下一个参数是字符数(用后面的参数替换*)
    printf("%9.*f\n",2,123.0);//下一个参数是小数点后的位数(用后面的参数替换*)

    return 0;
}

输出:
   123.00
      123
   123.00
3.hlL
类型修饰含义
hh单个字节
hshort
llong
lllong long
Llong double
4.type
type用于type用于
i或dintgfloat
uunsigned intGfloat
o八进制a或A十六进制浮点
x十六进制cchar
X字母大写的十六进制s字符串
f或Ffloat,6p指针
e或E指数n读入/写出的个数

scanf

  • %[flag]type
1.flag
flag含义flag含义
*跳过llong,double
数字最大字符数lllong long
hhcharLlong double
hshort
//格式化输入之*跳过
#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用于
dints字符串(单词)
i整数,可能为十六进制或八进制[…]所允许的字符
uunsigned intp指针
o八进制
x十六进制
a,e,f,gfloat
cchar
//格式化输入之%i
#include<stdio.h>
int main(int argc, char const *argv[])
{
    int num;
    scanf("%i",&num);
    printf("%d",num);
    
    return 0;
}
输入:
123           //10进制
输出:
123
输入:
0x12          //16进制
输出:
18
输入:
012           //8进制
输出:
10

printfscanf的返回值

  • 读入的项目数
  • 输出的字符数
  • 在要求严格的程序中,应该判断每次调用scanfprintf的返回值,从而了解程序运行中是否存在问题
//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    //编译当前.c文件,输出demo.exe可执行文件

D:\Developer\C_code\try>demo.exe    //执行demo.exe可执行文件
123456    //输入123456并回车
123456    //输出
D:\Developer\C_code\try>demo.exe > demo.txt   //执行demo.exe并将输出结果保存到demo.txt文件内
123456    //输入123456并回车

D:\Developer\C_code\try>type demo.txt   //使用type命令查看demo.txt文件的内容
123456    //demo.txt的内容
D:\Developer\C_code\try>demo.exe < demo.txt   //执行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
{
    ...
}
  • fopen函数的参数
参数效果
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)//判断是否正确打开,若打开失败fopen会返回NULL
    {
        int num;
        fscanf(fp, "%d",&num);
        printf("%d\n",num);
        fclose(fp);
    }
    else
    {
        printf("无法打开文件\n");
    }
    
    return 0;
}
输出:
123456

三、二进制文件

二进制文件

  • 其实所有的文件最终都是二进制的
  • 文本文件无非是用最简单的方式可以读写的文件
    • more、tail
    • cat
    • vi
  • 而二进制文件是需要专门的程序来读写的文件
  • 文本文件的输入输出是格式化,可能经过转码

文本VS二进制

  • Unix喜欢用文本文件来做数据存储和程序配置
    • 交互式终端的出现使得人们喜欢用文本和计算机"talk”
    • Unix的shell提供了一些读写文本的小程序
  • Windows喜欢用二进制文件
    • DOS是草根文化,并不继承和熟悉Unix文化
    • PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
  • 文本的优势是方便人类读写,而且跨平台
  • 文本的缺点是程序输入输出要经过格式化,开销大
  • 二进制的缺点是人类读写困难,而且不跨平台
    • int的大小不一致,大小端的问题…
  • 二进制的优点是程序读写快

程序为什么要文件

  • 配置
    • Unix用文本,Windows用注册表
  • 数据
    • 稍微有点量的数据都放数据库了
  • 媒体
    • 这个只能是二进制的
  • 现实是,程序通过第三方库来读写文件,很少直接读
    写二进制文件了

二进制读写

  • 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//若使用const int STR_LEN = 20;会报错variably modified 'name' at file scope

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);//sprintf向字符串‘format’输出“%19s”

    int i;
    for ( i = 0; i < number; i++)
    {
        printf("第%d个学生:\n",i);
        printf("\t姓名:");
        scanf(fomat, aStu[i].name);//format为sprintf产生的格式字符串
        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具有明确大小的类型
  • 更好的方案是用文本
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值