第13周:文件

本文详细介绍了C语言中的格式化输入输出控制、文件I/O操作,包括printf和scanf的使用,以及位运算符如按位与、或、异或、移位等的原理和示例。还讨论了文本文件与二进制文件的区别,以及如何处理二进制文件的读写和定位。
摘要由CSDN通过智能技术生成

13.1-1格式化输入输出格式

%[flags][width][.prec][hIL]type

Flag含义
-左对齐
+在前面放+或-
(space)正数留空
00填充
#include<stdio.h>

int main(int argc,char const *argv[])
{
    printf("%9d\n",123);//需要占9个字符的空间,123前还有6个空格
    printf("%-9d\n",123);//一样需要占9个字符,123后面还有6个空格
    printf("%+9d\n",123);//在123前面多上一个+,是可以0时跟左对齐同时进行的
    printf("%09d\n",123);//空格地方变成0,000000123
    
    return 0;
}
width或prec含义
number最小字符数
*下一个参数是字符数
.number小数点后的位数
.*下一个参数是小数点后的位数
#include<stdio.h>

int main(int argc,char const *argv[])
{
    printf("%9.2f\n",123.0);//占据9个字符且其中有两位小数
    printf("%*d\n",6,123);//让格式更灵活,6这个数是会替换到*号上的,具体效果就是占据6个字符
    
    return 0;
}
类型修饰含义
hh单个字节
hshort
llong
lllong long
Llong double
#include<stdio.h>

int main(int argc,char const *argv[])
{
    printf("%hhd\n",12345);//只能输入单个字符,输出多个报错
    printf("%hhd\n",(char)12345);//但可以强制类型转换,可以得到57的结果
    printf("%9.2f\n",123.0);
    
    return 0;
}
type用于type用于
i或者dintgfloat
uunsigned intGfloat
o八进制a或者A十六进制浮点
x十六进制cchar
X字母大写的十六进制s字符串
f或者Ffloat,6p指针
e或者E指数n输入/写出的个数
#include<stdio.h>

int main(int argc,char const *argv[])
{
    int num;
    printf("hhd%n\n",(char)12345,&num);
    printf(num);//以上的意思是,截至到%n为止前面有几位字符,会将多少位字符传递给指针&num的地址
    //然后在下面num就会输出2,因为上面(char)12345的值输出是57,是2位数
    
    return 0;
}

scanf:%[flag]type

flag含义flag含义
*跳过llong,double
数字最大字符数lllong long
hhcharLlong double
hshort
type用于type用于
dints字符串(单词)
i整数,可能为十六进制或八进制可以将读入的八进制和十六进制格式的数值转化为十进制[…]所允许的字符
uunsigned intp指针
o八进制
x十六进制
a,e,f,gfloat
cchar

printf和scanf的返回值

  1. 读入的项目数
  2. 输出的字符数
  3. 在要求严格的程序中,应该判断每次调用scanf或printf的返回值,从而了解程序运行中是否存在问题

13.1-3文件输入输出

文件输入输出

  1. 用>和<做重定向

FILE

  1. FILEfopen(const char**restrict path,const char*restrict mode);
  2. int fcolose(FILE*stream);
  3. fscanf(FILE,…)
  4. fprintf(FILE*,…)
  5. fprintf(FILE,…)

打开文件的标准代码

FILE*fp = fopen(file(文件名),“r”);

if(fp){
    fscanf(fp,...);
    fcolose(fp);
}else{
    ...
}
-----------------------------------------------------------------
    #include<stdio.h>
    
    int main(int argc,char const *argv[])
{
    FILE *fp = fopen("12.in","r");
    if( fp ){
        int num;
        fscanf(fp,"%d",&num);
        printf("%d\n",num);
        fclose(fp);
    }else{
        printf("无法打开文件\n");
    }
    return 0;
}

fopen

r打开只读
r+打开读写,从文件头开始
w打开只写。如果不存在则新建,如果存在则清空
w+打开读写。如果不存在则新建,如果存在则清空
a打开追加。如果不存在则新建,如果存在则从文件尾开始
…x只新建,如果文件已存在则不能打开(防止对文件造成破坏)

13.1-3二进制文件

  1. 其实所有的文件最终都是二进制的

  2. 文件无非是用最简单的方式可以读写的文件

    1. more、tail
    2. cat
    3. vi
  3. 而二进制文件是需要专门的程序来读写的文件

  4. 文本文件的输入输出是格式化,可能经过转码

文本文件 VS 二进制文件

  1. Unix喜欢用文本文件来做数据存储和程序配置

    1. 交互式终端的出现使得人们夏欢用文本和计算机“talk”
    2. Unix的shell提供了一些读写文本的小程序
  2. windows喜欢二进制文件

    1. DOS是草根文化,并不继承和熟悉Unix文化
    2. PC刚开始的时候能力有限,DOS的能力更有限,二进制进行输入输出更接近底层
优劣:
  1. 文本的优势是方便人类读写,而且跨平台

  2. 文本的缺点是程序输入输出要经过格式化,开销大

  3. 二进制的缺点是人类读写困难,而且不跨平台

    1. int的大小不一致,大小端的问题
  4. 二进制的优点是程序读写快

程序为什么要文件

  1. 配置

    1. Unix用文本,Windows用注册表
  2. 数据

    1. 稍微有点量的数据都放数据库了
  3. 媒体

    1. 这个只能是二进制的
  4. 现实是,程序通过第三方库来读写文件,很少直接读写二进制文件了

二进制读写(可跳过,底层)

  1. size_t fread(voidrestrict ptr,size_t size,size_t nitems,FILErestrict stream);
  2. size_t fwrite(const voidrestruct ptr,size_t size,size_t nitems,FILErestrict stream);
  3. 注意FILE指针是最后一个参数
  4. 返回的是成功读写的字节数
为什么nitems
  1. 因为二进制文件的读写一般都是通过对一个结构变量的操作来进行的
  2. 于是nitem就是用于说明这次读写几个结构变量!

在文件中定位

  1. long ftell(FILE*stream);

  2. int fseek(FILE*stream,long offset,int whence);

    1. SEEK_SET:从头开始
    2. SEEK_CUR:从当前位置开始
    3. SEEK_END:从尾开始(倒过来)
#include<stdio.h>
#include"student.h"

void read(FILE*fp,int index);

int main(int grac,char const grav[])
{
    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);
        fcolose(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 1:printf("其他\n");break;
        }
        printf("\t年龄:%d\n",stu.age);
    }
}

可移值性

  1. 这样的二进制文件不具有可移植性

    1. 在int为32位的机器上写成的数据文件无法直接在int为64的机器上正确读出
  2. 解决方法之一就是放弃使用int,而是使用typedef具有明确大小的类型

  3. 更好的方案是用文本

13.2*位运算

13.2-1按位运算

  1. C有这些按位运算的运算符:其实就是把整数的东西当做二进制来进行运算
·&按位的与
·|按位的或
·~按位取反
·^按位的异或
·<<左移
·>>右移
按位与&

image-20240423233237225

  1. 其实就是两组二进制的数,对应的数字必须都为1,新形成的数对应的数才会是1,否则就是0
  2. F:1111 E:1110,FE的作用就是使得跟他对应形成新的数最低位为0
按位或|

image-20240423233302557

  1. 也是两组二进制的数,对应的数字必须都为0,新形成的数对应的数才会是0,否则就是1。跟上面那个相反
按位取反~

image-20240423233328134

#include<stdio.h>

int main(int argc,char const *argv[])
{
    unsigned char c = 0xAA;
    printf("% c=%hhx\n",c);//aa
    printf("%~c=%hhx\n",(char)~c);//按位取反  55
    printf("%-c=%hhx\n",(char)-c);//补码   56
}
逻辑运算vs按位运算
  1. 对于逻辑运算,它只看到两个值:0和1

  2. 可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算

    1. 5&4——>4而5&&4——>1&1——>1
    2. 5|4——>5而5||4——>1|1——>1
    3. ~4——>3而!4——>!1——>0
按位异或^

image-20240423233342963

  1. 两组二进制,上下位数值对应相等为0,上下不相等为1
  2. 做两次相同的异或运算数值就翻回去了

移位运算

左移<<
  1. i << j
  2. i中所有的位向左移动j个位置,而右边填入0
  3. 所有小于int的类型,移位以int的方式来做,结果是int
  4. x <<= 1 等价于x*=2
  5. x <<= n 等价于x*=2的n次方
#include<stdio.h>

int main(int argc,char const *argv[])
{
    unsigned char c = 0xA5;
    printf("  c=%d\n",c);//165
    printf("c<<=%d\n",c<<2);//660
    return 0;
}
右移>>
  1. i >> j
  2. 所有小于int的类型,移位以int的方式来做,结果是int
  3. 对于unsigned的类型,左边填入0
  4. 对于signed的类型,左边填入原来的最高位(保持符号不变)
  5. x >>= 1 等价于x/=2
  6. x >>= n 等价于x/=2的n次方
#include<stdio.h>

int main(int argc,char const *argv[])
{
    int a = 0x80000000;
    unsigned int b = 0x80000000;
    printf("a=%d\n",a);//-2147483648
    printf("b=%u\n",b);//2147483648
    printf("a>>1=%d\n",a>>1);//-1073741824
    printf("b>>1=%u\n",b>>1);//1073741824
    return 0;
}
no zuo no die
  1. 移位的位数不要用负数,这是没有定义的行为

    1. x<<-2 //!!NO!!

13.2-3位运算例子

输出一个数的二进制

#include<stdio.h>

int main(int argc,char const *argv[])
{
    int number;
    scanf("%d",&number);
    number = 0x55555555;//输出01010101...,其实就是16个01(32个值)
    unsigned mask = 1u<<31;//int被省略但是其实是有生效的
    for(; mask ; mask >>=1 ){
        printf("%d",number & mask?1:0);
    }
    printf("\n");
    
    return 0;
}

13.2-4位段

  1. 把一个int的若干位组合成一个结构
struct{
    unsigned int leading : 3;//冒号后面的数字表示占几个比特
    
    unsigned int FLAG1:1;
    
    unsigned int FLAG2:1;
    
    int trailing:11;
};
  1. 可以直接用位段的成员名称来访问

    1. 比移位、与、或还方便
  2. 编译器会安排其中的位的排列,不具有可移植性

  3. 当所需的位超过一个int时会采用多个int

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值