【c基础】文件操作

news/2024/5/3 16:50:59

1.fopen和fclose函数

函数原型

 FILE *fopen(const char *path, const char *mode);

参数解释:

  • 返回值:fopen打开成功,则返回有效file的有效地址,失败返回NULL。
  • path是文件路径,可以相对路径,可以绝对路径
  • mode是模式
mode说明
r或者rtr以只读的方式打开文本文件,该文件必须存在,且是可读的。在一些系统上,r 模式也可以打开二进制文件,但这并不是标准行为。与 r 模式相比,rt 模式明确地指示打开文件为文本模式,但在大多数系统上,它与 r 模式的行为相同。
w或者wt打开只写文件,若文件存在则文件长度清0,即该文件内容会消失,若文件不存在则建 立该文件只写打开或建立一个文本文件,只允许写数据。
a或者at以追加的方式打开只写文件,若文件不存在则建立该文件,若文件存在,写入的内容将 被追加到文件末尾,即文件原先的内容会被保留。(EOF符保留)
rb只读打开一个二进制文件,只允许读数据
wb只写打开或建立一个二进制文件,只允许写数据
ab追加打开一个二进制文件,并在文件末尾写数据
r+或者rt+r+以可读写的方式打开,+号表示该文件必须存在。与 r+模式相比,rt+模式明确地指示打开文件为文本模式。
w+或者wt+打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失,若文件不存在 则建立该文件。
a+或者at+以追加的方式打开可读写文件,若文件不存在则建立该文件,若文件存在,写入的内 容将被追加到文件末尾,即文件原先的内容会被保留。(原来的EOF符不保留)
rb+读写打开一个二进制文件,允许读写数据,文件必须存在
wb+读写打开或建立一个二进制文件,允许读和写
ab+读写打开一个二进制文件,允许读,或在文件末追加数据
rw+读写打开一个文本文件,允许读和写

读写文件出错的一大原因可能是没有权限。


fopen打开读取的文件内容占用的内存在堆里面,用fclose可以释放掉。

fclose关闭fopen打开的文件。只要成功用fopen打开的文件,使用完成后就一定要调用fclose关闭。fclose的参数就是fopen的返回值。

一个程序同时可以打开的文件数是有限的,如果超过系统限制,那么打开文件会失败。

一个fopen会占用一些内存,多个就会对内存消耗很大。

所以记得要fopen使用完文件后及时的fclose。


代码示例:

#include <stdio.h>int main()
{// 1.判断文件是否能打开// FILE *p = fopen("./test.txt", "r");// char *filepath = "./test.txt";char filepath[] = "./test.txt";FILE *p = fopen(filepath, "w");if (p){printf("%p\n", p); // 00007FF98D85FA90printf("open success\n");}else{printf("open fail\n");}// 2.关闭文件if (p){fclose(p);printf("close success\n");}return 0;
}

2.文本文件与二进制文件的区别

基于字符编码vs基于值编码

计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异,简单来说,文本文件是基于字符编码的文 件,常见的编码有ASCII编码,UNICODE编码等等。二进制文件是基于值编码的文件,你可以根据具体应用,指定多少个比特代表一个值。

从上面可以看出文本文件基本上是定长编码的(也有非定长的编码如UTF-8)。而二进制文件可看成是变长编码的,因为是值编码嘛,多少个比特代表一个值,完全由你决定。

写入和读取时的区别

文本工具打开一个文件的过程是怎样的呢?拿记事本来说,它首先读取文件物理上所对应的二进 制比特流,然后按照你所选择的解码方式来解释这个流,然后将解释结果显示出来。

一般来说, 你选取的解码方式会是ASCII码形式(ASCII码的一个字符是8个比特),接下来,它8个比特8 个比特地来解释这个文件流。

例如对于这么一个文件流"01000000_01000001_01000010_01000011"(下划线''_'',为了增强可读性手动添加的), 第一个8比特''01000000''按ASCII码来解码的话,所对应的字符是字符''A'',同理其它3个8比特 可分别解码为''BCD'',即这个文件流可解释成“ABCD”,然后记事本就将这个“ABCD”显示在屏 幕上。

再比如文件流''00000000_00000000_00000000_00000001''可能在二进制文件中对应的是一 个四字节的整数int 1,在记事本里解释就变成了"NULL_NULL_NULL_SOH"这四个控制符。

字符数据本身在内存中就经过了编码,所以无论是二进制还是文本形式都是一样的,而对于非字 符数据来说,例如char i=10;如果用二进制来进行存储的话为00001010,但是如果需要用文本形式来 进行存储的话就必须进行格式化编码(对1和0分别编码,即形式为‘1’和‘0’分别对应的码值,即00000001 和 00000000写入文件中)。

  • FILE *writep = fopen("a.dat", "w"); //"w"按文本文件的字符编码写入,比如写入数字 10 (char类型), 则会将字符'1'和'0'对应的ASCII编码,即00000001 和 00000000依次写入 a.dat中
  • FILE *writep = fopen("a.dat", "wb"); //"wb"按二进制文件的二进制编码写入,比如写入 数字 10(char类型), 会将10对应的二进制编码 比如 00001010 写入 a.dat中

文本文件的存储与其读取基本上是个逆过程。而二进制文件的存取显然与文本文件的存取差不 多,只是编/解码方式不同而已。

优缺点比较

  • 一般认为,文本文件编码基于字符定长,译码容易些;二进制文件编码是变长的,所以它灵活, 存储利用率要高些,译码难一些(不同的二进制文件格式,有不同的译码方式)。
  • 文本文件每条数据通常是固定长度的。以ASCII为例,每条数据(每个字符)都是1个字节。二进制 进制文件每条数据不固定。如short占两个字节,int占四个字节,float占8个字节。
  • 文本文件只能存储char型字符变量。二进制文件可以存储char/int/short/long/float/……各种 变量值。
  • 在windows下,文本文件不一定是一ASCII来存贮的,因为ASCII码只能表示128的标识,你打开一个 txt文档,然后另存为,有个选项是编码,可以选择存贮格式,一般来说UTF-8编码格式兼容性要好一 些,而二进制用的计算机原始语言,不存贮兼容性。

3.win和linux文本文件中换行符的区别

windows读写文件时

  • windows所有的文本文件都是\r\n结尾的,而不是\n结尾的,所以这里要占2个字节。
    • 写入时,如果是文本方式写入即用"w"参数时,每遇到一个''\n'',它将其换成''\r \n'', 然后再写入文件;
    • 读取时,当文本读取即用"r"参数时,它每遇到一个''\r\n''将其反变化为''\n'',然后送到 读缓冲区。
  • 对于二进制文件,正常读写,不存在任何转换。

linux读写文件时:

  • linux所有的文本文件,本来就是\n结尾的,前面没有\r,参数b在linux是无效的
  • 二进制文件,正常读写,不存在任何转换

windows和Linux间文本文件格式转换

  • linux转windows:unix2dos -n a.txt b.txt。将a.txt 中的\n变为\r\n,然后另存为b.txt。
  • windows转linux:dos2unix -n b.txt c.txt。将b.txt 中的\r\n变为\n,然后另存为c.txt。

示例代码理解windows下"r"的存在

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>int main()
{// 1.打印每个字符和对应的ASCII码FILE *filep = fopen("./add.txt", "r");unsigned char a = 0;while (1){fread(&a, 1, sizeof(char), filep);if (feof(filep)){break;}printf("(%c,%d)\n", a, a);}fclose(filep);// 2.获取文件大小(注意'\r'也占用了一个字节)struct stat st = {0};stat("./add.txt", &st);printf("size=%d\n", st.st_size); // size=12// 'h' 'e' 'l' 'l' 'o' '\r' '\n' 'w' 'o' 'r' 'l' 'd'return (0);
}
/*
(h,104)
(e,101)
(l,108)
(l,108)
(o,111)
(
,10)
(w,119)
(o,111)
(r,114)
(l,108)
(d,100)
size=12
*/

4.getc和putc函数

getc

已字节为单位读写文件。

函数原型

char getc(FILE *stream);

说明:

  • getc的参数是fopen成功打开文件后返回的指针,getc的返回值是一个char
  • getc的功能是以字节为单位读取文件内容。注意当文本文件中存在汉字内容时,读取会出现乱码,因为一个汉字占2个字节(gbk编码时)或者3个字节(utf-8编码时)。
  • 文本文件的最后结束标示是-1,是一个宏常量EOF
#define EOF -1

示例代码

#include <stdio.h>int main()
{// 1.判断文件是否能打开char filepath[] = "./test.txt";FILE *p = fopen(filepath, "r");if (p){printf("open %s success\n", filepath);// 2.读取内容char c = getc(p); // 读取一个字节内容printf("%c\n", c);c = getc(p); // 再读取一个字节内容printf("%c\n", c); }else{printf("open %s fail\n", filepath);}// 3.关闭文件if (p){fclose(p);printf("close success\n");}return 0;
}

用循环打印文件所有内容

#define EOF -1

EOF不是文件的一部分内容,只是文件结束的标识

#include <stdio.h>int print_file(char *filepath)
{char c;FILE *p = fopen(filepath, "r");if (p){printf("open %s success\n", filepath);c = getc(p);while (c != EOF){printf("%c", c);c = getc(p);}fclose(p);}else{printf("open %s fail\n", filepath);return -1;}return 0;
}int main()
{// 打开文件//char filepath[] = "./test.txt";//char filepath[] = "./test.c";char filepath[] = "./test.txt";int ret = print_file(filepath);printf("ret=%d\n", ret);return 0;
}

自定义读取文件一行内容的函数

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int read_line(char *filepath, char **file_data)
{FILE *filep = fopen(filepath, "r");if (!filep){perror("fopen");return -1;}int line_size = 1;char *content = (char *)malloc(line_size);if (content == NULL){perror("malloc");fclose(filep);return -2;}// 初始化content为空字符串 content[0] = '\0';int count = 0;int is_finish = 0;int i = 0;while (1){char temp[1024] = {0};for (i = 0; i < 1023; i++){char c = getc(filep);if (c == '\n' || c == EOF){is_finish = 1;break;}temp[i] = c;count++;}if (count > 0) // 只有在读取到字符时才进行内存分配和字符串拼接{if (count >= line_size){char *ret = (char *)realloc(content, count+1);if (ret == NULL){perror("realloc");free(content); // 释放之前的内存fclose(filep);return -3;}content = ret;line_size = count + 1;}strcat(content, temp);// 添加空字符,确保字符串正确终止content[count] = '\0'; }if (is_finish){break;}}// printf("(%s)\n", content);   // 文件内容// printf("count=%d\n", count); // 字节数*file_data = content;fclose(filep);return 0;
}int main()
{char filepath[100] = "./test.txt";char *file_content;int ret = read_line(filepath, &file_content);printf("ret = %d\n", ret);if (ret == 0){printf("(%s)\n", file_content);printf("content len=%d\n", strlen(file_content));free(file_content);  }else{printf("Error reading line from file\n");  }return 0;
}

putc

函数原型

int putc(int c, FILE *stream);

第一个参数是要写入的char, 第二个参数是fopen返回的文件指针

getc参数的文件指针必须是用r模式打开,putc必须是用w模式打开

#include <stdio.h>int main()
{// 打开文件char filepath[] = "./test.txt";FILE *filep = fopen(filepath, "w");if (filep){// 写入内容putc('a', filep);putc('\n', filep);putc('b', filep);putc('c', filep);// 关闭文件fclose(filep);}else{printf("open fail\n");}return 0;
}

自定义将字符串写入文件的函数

#include <stdio.h>
#include <string.h>void write_str(char *filepath, const char *s, unsigned int len)
{FILE *filep = fopen(filepath, "w");if (filep){for (int i = 0; i < len; i++){printf("%c", s[i]);putc(s[i], filep);}fclose(filep);}else{printf("open fail\n");}
}int main()
{char filepath[] = "./test.txt";char mystr[50] = "hello world";int len;// len = sizeof(mystr)/sizeof(mystr[0]);len = strlen(mystr); //用字符串长度write_str(filepath, mystr, len);return 0;
}

自定义文件拷贝函数实现命令行命令

使用c语言的main函数还可以自定义命令行的命令

比如可执行文件的名字叫 copyfile,通过设计源代码的main函数可以达到命令行的作用

  1. gcc test.c -o copyfile
  2. copyfile test.txt test_write.txt
  3. 可以实现将test.txt拷贝为test_write.txt

示例代码

#include <stdio.h>void copyfile(char *srcPath, char *tarPath)
{FILE *readp = fopen(srcPath, "r");FILE *writep = fopen(tarPath, "w");if (readp && writep){while (1){char c = getc(readp);if (c == EOF){break;}putc(c, writep);}}fclose(readp);fclose(writep);
}int test()
{char filepath[] = "./test.txt";char writePath[] = "./test_write.txt";copyfile(filepath, writePath);return 0;
}int main(int argc, char **args)
{if (argc < 3){printf("params less than 3");return -1;}copyfile(args[1], args[2]);return 0;
}

运行

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main test.txt test_copy.txt

简单的文件加密和解密

加密:将一份文件拷贝时,给每个字符通过加减乘除或者指定函数等指定规则变为另一个字符再写入另一个文件。

解密:按加密的方式逆向获得原字符。

示例代码:

#include <stdio.h>void copyfile(char *srcPath, char *tarPath, char isEncode)
{FILE *readp = fopen(srcPath, "r");FILE *writep = fopen(tarPath, "w");if (readp && writep){while (1){char c = getc(readp);if (c == EOF){break;}if (isEncode == '+')c++;elsec--;putc(c, writep);}}fclose(readp);fclose(writep);
}int test()
{char filepath[] = "./test.txt";char writePath[] = "./test_write.txt";copyfile(filepath, writePath, '+');return 0;
}int main(int argc, char *argv[])
{if (argc < 4){printf("params less than 3");return -1;}copyfile(argv[1], argv[2], argv[3][0]);return 0;
}

运行效果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main test.txt test_encode.txt +
[root@localhost test]# cat test.txt
hello world
[root@localhost test]# cat test_encode.txt 
ifmmp!xpsme[root@localhost test]# 
[root@localhost test]# ./main test_encode.txt test_decode.txt -
[root@localhost test]# cat test_decode.txt 
hello world
[root@localhost test]# 

5.EOF与feof函数

程序怎么才能知道已经到结尾了呢?文本文件内容的最后一个char是EOF。

EOF不属于文件的内容,只是文件的结尾标识,而且也不要直接用-1来替代EOF。

只有文本文件才可以通过EOF来判断文件的结尾标示,文本文件的结尾标示是EOF,但二进制文件不是,对于二进制文件EOF是无效的,二进制文件就需要用feof这个函数。feof也可以用在文本文件。

函数原型:

int feof(FILE *stream);

参数就是fopen函数返回的指针。

文件指针所在位置:初始在第一行的开头,即第1个字符前面那。后续每次读取了内容,就在已读取内容的最后一个字符和下一个字符之间。读取内容的最后一个字符是当前行的换行符时,读取后,文件指针在下一行的开头。

feof的原理:EOF在最后一行末尾,在最后一行时,feof(filep)不会为True,读文件的函数不仅会把最后一行的文件内容读到,也会把EOF读到。示例代码理解:

#include <stdio.h>// 1.用feof
int main01()
{char filepath[] = "./test.txt";FILE *p = fopen(filepath, "r");int count = 0;if (p){while (!feof(p)){char c = getc(p);// if (c == EOF){break;} // 不读取最后一个字符,即文件结尾符EOF// 打印字符和对应的ASCII码printf("(%c,%d)=>", c, c);count++;}fclose(p);}else{printf("open fail\n");}printf("count = %d\n", count); // count = 8return 0;
}// 2.用EOF
int main02()
{char filepath[] = "./test.txt";FILE *p = fopen(filepath, "r");int count = 0;char c = getc(p);if (p){while (c != EOF){// 打印字符和对应的ASCII码printf("(%c,%d)=>", c, c);count++;c = getc(p);}fclose(p);}else{printf("open fail\n");}printf("count = %d\n", count); // count = 7return 0;
}int main()
{main01();main02();return 0;
}

运行结果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# cat test.txt
hello world
[root@localhost test]# ./main
(h,104)=>(e,101)=>(l,108)=>(l,108)=>(o,111)=>( ,32)=>(w,119)=>(o,111)=>(r,114)=>(l,108)=>(d,100)=>(
,10)=>(�,-1)=>count = 13
(h,104)=>(e,101)=>(l,108)=>(l,108)=>(o,111)=>( ,32)=>(w,119)=>(o,111)=>(r,114)=>(l,108)=>(d,100)=>(
,10)=>count = 12
[root@localhost test]# 

6.fputs和fgets 按照行读写文件

fputs 将字符串写入文件

函数原型

int fputs(const char *str, FILE *stream);

该函数返回一个非负值,如果发生错误则返回 EOF(-1)。

示例代码:

#include <stdio.h>int main()
{FILE *filep = fopen("./fputs.txt", "w");if (filep){fputs("hello world\n", filep);fputs("xxxx\n", filep);char info[10] = {0};sprintf(info, "work %d%d%d\n", 9, 9, 6);fputs(info, filep);fclose(filep);}else{perror("open file fail");}return 0;
}

fgets 从键盘输入或者文件输入

函数原型

char *fgets(char *s, int size, FILE *stream);

参数:

  • s: 字符型指针,指向存储读入数据的缓冲区的地址
  • size: 从流中读入 size - 1 个字符,放入s的前size-1个位置,然后将第size个位置处放'\0' 字符。
  • stream: 指向读取的流。如果要从键盘输入,则传入stdin即可。如果要将文件作为输入来源,则传入对应的文件指针即可。

返回值:

  • 如果读入错误或遇到文件结尾(EOF),则返回NULL
  • fgets的返回值是 char *,代表函数读到的字符串的首地址,如果fgets到了文件末尾,继续调 用,返回NULL

fgets读取原理:

  • fgets 执行时会从文件流的当前位置读取 size - 1 个字符,然后在读取到的这些字符后面添加 一个 '\0' 字符并将这些字符放入 s 中,也就是第size位置处放'\0' 字符,作为字符串的结尾。
  • 如果 fgets 在执行时发现文件流的当前位置到换行符或文件末尾之间的这些字符不够 size - 1 个字符,则将不够数的字符统统用 '\0' 代替。
  • 换行符也是当前行的内容
#include <stdio.h>
#include <string.h>int main1()
{FILE *filep = fopen("./fputs.txt", "w");if (filep){while (1){// get data from keyboardchar buf[1024] = {0};fgets(buf, sizeof(buf), stdin);if (strncmp(buf, "exit", 4) == 0)break;// write data into filefputs(buf, filep);}fclose(filep);}return 0;
}int main2()
{FILE *filep = fopen("./fputs.txt", "r");char buff[10] = {0};if (filep){// read data line by line from filewhile (fgets(buff, sizeof(buff), filep) != NULL){printf("(%s)", buff);}fclose(filep);}return 0;
}int main()
{main1();main2();return 0;
}

运行效果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main
hello world
work for life
exit
(hello wor)(ld
)(work for )(life
)[root@localhost test]# 

用fgets和fputs进行文件拷贝

#include <stdio.h>void copyfile(char *srcPath, char *tarPath)
{FILE *readp = fopen(srcPath, "r");if (!readp){printf("fopen %s fail\n", srcPath);return;}FILE *writep = fopen(tarPath, "w");if (readp && writep){while (!feof(readp)){char buff[1024] = {0};if (fgets(buff, sizeof(buff), readp) == NULL){break;}// printf("(%s)\n", buff);fputs(buff, writep);}fclose(readp);fclose(writep);}}int main()
{char filepath[] = "./test.txt";char writePath[] = "./test_write.txt";copyfile(filepath, writePath);return 0;
}

7.fprintf和fscanf 按指定格式读写文件

fscanf从文件内容中读取指定的内容

fprintf输出指定格式内容到文件中

int fscanf(FILE *stream, const char *format, [argument...])
int fprintf(FILE *stream, const char *format, [ argument ]...)

fscanf函数返回值:

  • 读文件成功,则返回成功读取的项数
  • 读文件失败,则返回EOF

fprintf()函数返回值

  • 写文件成功,则返回写入的总字符数
  • 写文件失败,则返回负值

解析文件内容并追加结果

cal.txt

12+3=
14*6=
5/7=
32-56=

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int myfunc(int a, char b, int c)
{switch (b){case '+':return a + c;case '-':return a - c;case '*':return a * c;case '/':if (c > 0)return a / c;elsereturn 0;default:printf("error");return 0;}
}int main()
{FILE *filep = fopen("./cal.txt", "r");FILE *writep = fopen("./cal_f.txt", "w");if (filep && writep){while (1){int a, c;char b;int ret;//读取文件一行ret = fscanf(filep, "%d%c%d=", &a, &b, &c);printf("\nret=%d\n", ret);/*当前文件最后一行具有内容时,此时EOF在最后一行末尾文件指针在最后一行时,feof(filep)会为真,fgets调用buff也会读取到内容,在fgets调用后feof(filep)会为假因此判断feof(filep)和处理内容谁前谁后很关键先处理内容,再判断feof(filep)是否退出*/// 处理内容if (ret == 3){printf("%d%c%d=%d\n", a, b, c, myfunc(a, b, c));fprintf(writep, "%d%c%d=%d\n", a, b, c, myfunc(a, b, c));}// 判断文件是否结束if (feof(filep)){break;}}fclose(filep);fclose(writep);}return 0;
}

cal_f.txt

12+3=15
14*6=84
5/7=0
32-56=-24

文件内容特定条件查找

文件:name_age.txt

name=liudehua,age=50
name=anbei,age=30
name=zhangxueyou,age=45
name=canglaoshi,age=70

要求:打印年龄第二大的人的姓名

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define NUM 100int main()
{FILE *filep = fopen("./name_age.txt", "r");int maxage = 0;int max2age = -1;char maxname[100] = {0};char max2name[100] = {0};if (filep){while (1){printf("------------------\n");//读取文件一行char buff[1024] = {0};fgets(buff, sizeof(buff), filep);/*当前文件最后一行具有内容时,此时EOF在最后一行末尾文件指针在最后一行时,feof(filep)会为真,fgets调用buff也会读取到内容,在fgets调用后feof(filep)会为假因此判断feof(filep)和处理内容谁前谁后很关键先处理内容,再判断feof(filep)是否退出*/printf("(%s)\n", buff);if (strncmp(buff, "name", 4) == 0){//用,分割得到姓名char *s;s = strtok(buff, ",");printf("%s\n", s);char name[100] = {0};sscanf(s, "name=%s", name);printf("%s\n", name);//用,分割得到年龄s = strtok(NULL, ",");printf("%s", s);int age = 0;sscanf(s, "age=%d", &age);printf("%d\n", age);//更新最大值和姓名if (age > maxage){strcpy(max2name, maxname);strcpy(maxname, name);max2age = maxage;maxage = age;}else if ((max2age < age) && (age < maxage)){strcpy(max2name, name);max2age = age;}}if (feof(filep)){break;}}fclose(filep);}printf("------------------\n");printf("%s,%d\n", maxname, maxage);printf("%s,%d\n", max2name, max2age);return 0;
}

8.stat函数

头文件

#include <sys/stat.h>
#include <sys/types.h>

函数原型

int stat(const char * Filename, struct stat *_Stat)

参数:

  • 第一个参数代表文件名
  • 第二个参数是struct stat结构,传出参数
  • 返回值:执行成功返回0,失败返回-1

函数作用:通过传出参数,得到文件的属性,包括文件建立时间,文件大小,最后访问时间,最后修改时间等信息,比如stat.st_size;得到文件大小,单位字节

结构体struct stat的参数说明

struct stat
{dev_t st_dev; //device 文件的设备编号ino_t st_ino; //inode 文件的索引节点mode_t st_mode; //protection 文件的类型和存取的权限nlink_t st_nlink; //number of hard links 连到该文件的硬连接数目,//刚建立的文件值为1.uid_t st_uid; //user ID of owner 文件所有者的用户识别码gid_t st_gid; //group ID of owner 文件所有者的组识别码dev_t st_rdev; //device type 若此文件为装置设备文件, 则为其设备编号off_t st_size; //total size, in bytes 文件大小, 以字节计算unsigned long st_blksize;//blocksize for filesystem I/O 文件系统的I/O 缓冲区大小.unsigned long st_blocks;//number of blocks allocated 占用文件区块的个数,//每一区块大小为512 个字节.time_t st_atime; //time of lastaccess 文件最近一次被存取或被执行的时间,//一般只有在用mknod、utime、read、write 与tructate 时改变.time_t st_mtime; //time of last modification 文件最后一次被修改的时间,// 一般只有在用mknod、utime 和write 时才会改变time_t st_ctime; //time of last change i-node 最近一次被更改的时间,//此参数会在文件所有者、组、权限被更改时更新
};

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
//#include <unistd.h>
#include <time.h>
#define NUM 100int main()
{struct stat st = {0};int ret = stat("test.txt", &st);//调用完stat函数后,就得到了文件的各种属性printf("%d\n", ret); // 0if (ret == 0){//文件大小int size = st.st_size;printf("%d\n", size); // 28struct tm *info;char buffer[80];//最近一次被执行的时间time_t atime = st.st_atime;printf("%d\n", atime); // 1668269249info = localtime(&atime);strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);printf("%s\n", buffer); // 2022-11-13 00:07:29//最后一次被修改的时间time_t mtime = st.st_mtime;printf("%d\n", mtime); // 1668261399info = localtime(&mtime);strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);printf("%s\n", buffer); // 2022-11-12 21:56:39//最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新time_t ctime = st.st_ctime;printf("%d\n", ctime); // 1668252245info = localtime(&mtime);strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);printf("%s\n", buffer); // 2022-11-12 21:56:39}else{printf("stat error\n");}return 0;
}

9.fread和fwrite 读写二进制数据

函数说明

fgets fputs fprintf, fscanf这些函数都是针对文本文件读写的,不能对一个二进制文件进行读写。

除了文本文件之外的文件都是二进制,比如图像,可执行程序,音乐,视频这些都是二进制 的(其实文本文件是特殊的二进制文件,只是编码方式不同而已,对于数值类型,文本文件 按字符编码,二进制文件按值编码)

fprintf等都是往文本文件里面写一个字符串,比如将123456写入文件,实际上把‘1’, ‘2’, '3', '4', '5', '6'这6个字符写入文件。如果要把一个int整数写入文件,这个文件就不是文本文件了。

函数原型

size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
size_t fwrite(void *buffer, size_t size, size_t count, FILE *stream);

参数:

  • 第一个参数是buff的内存地址,表示要写什么
  • 第二个参数是每个单位的大小
  • 第三个参数是写 多少个单位(只需要第二个参数和第三个参数的乘积与 第一个参数指定的内存区域一样大即可)
  • 第四个参数是fopen返回的文件指针。

这个函数以二进制形式对文件进行操作,不局限于文本文件

返回值:返回实际写入的数据块数目

示例代码

#include <stdio.h>int main1()
{FILE *filep = fopen("./test.dat", "w");int i = 100;fwrite(&i, 1, sizeof(int), filep);short x = 50;fwrite(&x, 1, sizeof(short), filep);fclose(filep);return (0);
}int main2()
{FILE *filep = fopen("./test.dat", "r");int a = 0;fread(&a, 1, sizeof(int), filep);printf("a=(%d)\n", a);short b = 0;fread(&b, 1, sizeof(short), filep);printf("b=(%d)\n", b);fclose(filep);return (0);
}int main()
{// 1.fwritemain1();// 2.freadmain2();return 0;
}

fread的返回值

  • fread第二个参数代表了一个单位多大,第三个参数一次要读多少单位
  • fread返回值是成功读取到的单位个数。fread的返回值是,实际读到的内容大小/单位大 小,比如函数读的单位是2个字节,实际读了8个字节的内容,则fread返回4。再比如单位是 4个字节,但只读了2个字节的内容,则返回0
  • 当fread的第二个参数是1时,可以认为fread返回的是读取到的字节数
#include <stdio.h>
int main()
{FILE *filep = fopen("./test.dat", "r");int a = 0;while (1){int res = fread(&a, 1, sizeof(int), filep);printf("%d\n", res); //4if (feof(filep)){break;}printf("%d\n", a);}fclose(filep);return (0);
}

实现二进制文件的拷贝

比如图像,可执行程序,音乐,视频这些都是二进制的,都可以用程序拷贝

一个字节一个字节的拷贝

下面代码速度较慢,因为是读一个字节,写一个字节

#include <stdio.h>int main()
{FILE *readp = fopen("./test.txt", "rb");if (readp){printf("open read");}FILE *writep = fopen("./test2.txt", "wb");if (writep){printf("open write");}while (1){char a = 0;fread(&a, 1, 1, readp);if (feof(readp)){break;}fwrite(&a, 1, 1, writep);}fclose(readp);fclose(writep);return 0;
}

用栈每次拷贝多个字节

读1024个字节,写1024个字节,不够1024时,读多少写多少

#include <stdio.h>int main()
{FILE *readp = fopen("./test.txt", "rb");FILE *writep = fopen("./test2.txt", "wb");if (writep && readp){while (!(feof(writep))){char a[1024] = {0};int res = fread(&a, 1, sizeof(a), readp);fwrite(&a, 1, res, writep); //用res读多少,写多少}}fclose(readp);fclose(writep);return 0;
}

用堆动态分配内存拷贝

可以根据文件大小,动态分配一个堆内存出来

但是如果这个文件的内存很大一下把内存撑爆了

可以得到文件size后,判断size大小决定是否分成几次来写

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>#define NUM 1024 * 64 //64 kint main()
{FILE *readp = fopen("./test.txt", "rb");FILE *writep = fopen("./test3.txt", "wb");if (writep && readp){struct stat st = {0};stat("test.txt", &st);int size = st.st_size;if (size >= NUM){size = NUM;}char *buff = malloc(size);while (!(feof(readp))){//char buff[1024] = {0};int res = fread(buff, 1, size, readp);fwrite(buff, 1, res, writep);}free(buff);fclose(readp);fclose(writep);printf("success");}return 0;
}

读写结构体数据

将结构体数据写入文件中,读取时直接就拿到这个结构体,方便。

把一个结构变量写入文件的时候,结构变量成员的对齐内存浪费部分同样也会写入文件。

#include <stdio.h>
#include <stdlib.h>struct man
{char name[20];int age;
};int main()
{//将结构体等数据写入dat文件char filename[] = "xxx.dat";FILE *writep = fopen(filename, "wb");struct man m = {"tom", 12};fwrite(&m, 1, sizeof(struct man), writep);int x = 100;fwrite(&x, 1, sizeof(x), writep);fclose(writep);//从dat中读取数据FILE *readp = fopen(filename, "rb");struct man newm;fread(&newm, 1, sizeof(newm), readp);printf("%s, %d\n", newm.name, newm.age); // tom, 12int j = 0;fread(&j, 1, sizeof(int), readp); // j = 100printf("j = %d\n", j);fclose(readp);//读到堆中readp = fopen(filename, "rb");struct man *datap = malloc(sizeof(struct man));fread(datap, 1, sizeof(struct man), readp);printf("%s, %d\n", datap->name, datap->age); // tom, 12free(datap);int *intp = malloc(sizeof(int));fread(intp, 1, sizeof(intp), readp);printf("%d\n", *intp); // 100free(intp);fclose(readp);return 0;
}

10.fseek函数

FILE指针原理

  • FILE结构内部是有一个指针的,每次调用文件读写函数,这些函数就会自动移动这个指针 默认情况下指针只能从前往后移动。
  • c语言文件读写库函数都是自动维护FILE里面的相关成员,包括文件读写当前位置。 每次调用fgets,或者fread,下次再调用的时候,就会自动从已读取内容后面开始。
  • feof为什么能知道是否到了文件结尾了呢?fread或者fgets这些函数如果读完文件所有内容,他们会设置FILE里面相关变量的值。
  • feof只是判断FILE结构里面相关变量的值是否为文件已经结尾状态。
  • fopen成功打开一个文件后,文件指针默认都是在0 SEEK_SET这个位置。

fseek函数使用与技巧

事实上这个指针位置我们可以自己来调,需要用fseek这个函数。

int feek(FILE *_File, long _Offset, int _Origin);
  • 第一个参数是fopen返回的文件指针
  • 第二个参数是偏移量。正数表示向后偏移,负数表示向前偏移。
  • 第三个参数是设置从哪里开始偏 移,可能取值为:SEEK_CUR,SEEK_END或者SEEK_SET。
SEEK_SET: 文件开头
SEEK_CUR:文件当前位置
SEEK_END:文件结尾

函数作用:设置文件的指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset个字节的位置,函数返回0。如果执行失败则不改变stream指向的位置,函数返 回一个非0值。

一些小技巧

  • fseek(readp, 0, SEEK_SET); 回到文件开始位置
  • fseek(readp, 0, SEEK_END); 回到文件结尾位置

注意事项

  • 实验得出,超出文件末尾位置,还是返回0,往回偏移超出首位置,还是返回0,请小心使 用。
  • 并且超出文件末尾或者首位置,读到的数据都是0

示例代码理解

#include <stdio.h>
#include <stdlib.h>int main()
{FILE *writep = fopen("a.dat", "w");char a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};fwrite(a, 1, sizeof(a), writep);fclose(writep);FILE *readp = fopen("a.dat", "r");fseek(readp, 2, SEEK_SET);// while (!feof(readp))for (int i = 0; i < 2; i++){char a[2];fread(a, 1, sizeof(a), readp);printf("%d, %d\n", a[0], a[1]);}fclose(readp);/*3, 45, 6*/return 0;
}

用fseek快速生成超大文件

快速生成一个超大文件,因为需要经常测试代码的文件读写情况。

原始的慢方法

#include <stdio.h>
#include <stdlib.h>int main()
{FILE *writep = fopen("a.dat", "w");for (int i = 0; i < 10000000; i++){char a = 0;fwrite(&a, 1, sizeof(a), writep);}fclose(writep);return 0;
}

用fseek技巧快速生成(超大空文件)

#include <stdio.h>
#include <stdlib.h>int main()
{FILE *writep = fopen("a.dat", "w");fseek(writep, 1000000, SEEK_SET);char a = 0;fwrite(&a, 1, sizeof(a), writep);fclose(writep);return 0;
}

11.ftell函数

函数ftell用于得到文件指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁地前后移动,程序不容易确定文件文件的当前位置。

long loc = ftell(fp);

以下代码通过ftell函数和feof函数,可以得出:

  • 文件的EOF在最后一行末尾,无论最后一行是"文件内容+EOF",还是只有"EOF", feof(readp)都为假
  • fread读最后一块内容时:
    • 如果是"文件内容+EOF",则会去掉EOF
    • 如果是只有"EOF",则读不到任何数据,此时用fread函数不会修改传入地址的值

示例代码

#include <stdio.h>
#include <stdlib.h>int main()
{FILE *writep = fopen("a.dat", "w");char a[3] = {1, 2, 3};fwrite(a, 1, sizeof(a), writep);fclose(writep);printf("--------------\n");FILE *readp = fopen("a.dat", "r");//fseek(readp, 0, SEEK_SET);long loc = ftell(readp);printf("loc = %lu\n", loc); //loc = 0printf("--------------\n");char b;while (!feof(readp)){fread(&b, 1, 1, readp);printf("%d\n",b);long loc = ftell(readp);printf("loc = %lu\n", loc);}printf("--------------\n");fseek(readp, 0, SEEK_END);loc = ftell(readp);printf("loc = %lu\n", loc);char x = 0;fread(&x, 1, 1, readp);printf("x = %d\n",x);printf("--------------\n");fseek(readp, -1, SEEK_END);loc = ftell(readp);printf("loc = %lu\n", loc);char y = 0;fread(&y, 1, 1, readp);printf("y = %d\n", y);fclose(readp);return 0;
}

运行结果

--------------
loc = 0
--------------
1
loc = 1
2
loc = 2
3
loc = 3
3
loc = 3
--------------
loc = 3
x = 0
--------------
loc = 2
y = 3

12.fflush函数和文件读写缓冲区

c语言所有的文件操作函数都是缓冲区函数

以下代码只有在fclose(writep);后文件a.txt中才会有内容,说明是缓冲区库函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{FILE *writep = fopen("a.txt", "w");while (1){char a[100] = {0};scanf("%s", a);if (strcmp(a, "exit") == 0){break;}fprintf(writep, "%s\n", a);}fclose(writep);return 0;
}

用ffush函数跳过内存直接写入磁盘

int fflush(FILE *_File)

由于fflush是实时的将缓冲区的内容写入磁盘,所以不要大量去使用,因为fflush相当于是直接 用磁盘读写而不是通过内存再到磁盘,所以速度慢,而且频繁读写磁盘会降低磁盘寿命,但如果 是重要的数据,可以用fflush写入磁盘。

13.remove 删除文件

删除指定文件

int remove(const char *Filename);

没有该文件时不会报错

#include <stdio.h>int main()
{char filename[] = "xxx.txt";FILE *writep = fopen(filename, "w");fclose(writep);remove(filename);return 0;
}

14.rename 文件改名或者剪切

int rename(const char *OldFilename, const char *NewFilename);

示例代码

#include <stdio.h>
int main()
{char filename[] = "xxx.txt";FILE *writep = fopen(filename, "w");fclose(writep);char newname[] = "../yyy.txt";rename(filename, newname);return 0;
}



http://www.mrgr.cn/p/45755882

相关文章

前端【小程序】14-小程序基础篇【地理位置】【腾讯定位服务】

腾讯定位服务官网:https://lbs.qq.com  位置服务(LBS)是基于用户的位置来提供服务的技术,通过要配合第三方的服务来实现,如腾讯地图、高德地图、百度地图等,享+项目采用的是腾讯的位置服务。  申请使用腾讯位置服务需要按如下步骤操作: 注册账号 创建应用 生成 key …

短视频app开发,可以借鉴的几个登录界面设计

登录界面一:<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Business Login</title>&l…

项目常用工具类

Java本身自带了许多非常好用的工具类&#xff0c;但有时我们的业务千奇百怪&#xff0c;自带的工具类又无法满足业务需求&#xff0c;需要在这些工具类的基础上进行二次封装改造。以下是在实际工作中&#xff0c;可能会使用到的工具类。 一、对象的序列化和反序列化 对象的序…

乡政府管理系统|基于Springboot的乡政府管理系统设计与实现(源码+数据库+文档)

乡政府管理系统目录 目录 基于Springboot的乡政府管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、活动信息管理 3、新闻类型管理 4、新闻动态管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推…

树4-树的确定与#号创建

树4-树的确定与#号创建只有中序遍历不能确定一个树确定树的方法中序遍历确定左右区域,而先序和后序确定实际位置二叉树的#创建二叉树结点 typedef struct BinaryNode{char ch;struct BinaryNode *lChild;struct BinaryNode *rChild; } BinaryNode;二叉树遍历 void Recursion(Bi…

Sony Camera Remote SDK在Windows上的使用

Sony官方提供了相机遥控软件开发包&#xff0c;允许用户自行开发应用软件&#xff0c;实现对相机的远程控制&#xff0c;包括拍摄、监看和文件传输等。截至目前最新的版本是2024.4.12发布的1.12.00版本&#xff0c;下载链接如下&#xff1a;Camera Remote SDK | LICENSE AGREEM…

4 20复盘

显示的是springboot无法注入这个bean。 登录后使用jwt并根据当前用户信息生成个人token,然后把这个token存到redis中,访问其他接口时,在拦截器中分别从浏览器请求头和redis取出token,有的话才能放行。修改密码后将redis中token取出。 为了验证这个爆红信息,我走了一遍登录,…

weblogic JSP action的配置

action(如xxx.do&#xff09;可以在Java文件中通过注解的方式配置&#xff0c;也可以在web.xml中进行配置 在java文件中配置的场合 WebServlet(xxxx.do) 并实现支持的方法&#xff1a;doGet或doPost等 或者 WebServlet(xxxx.do) 并实现service方法 所有method的处理方法都会…

chatgpt免费使用网站

在人工智能的浪潮中&#xff0c;OpenAI的ChatGPT作为一款前沿的语言处理工具&#xff0c;已经引起了广泛的关注和讨论。 ChatGPT以其卓越的语言理解和生成能力&#xff0c;为用户提供了多样化的应用场景&#xff0c;从日常对话、编程辅助到内容创作等。然而&#xff0c;对于许…

论婚恋相亲交友软件的市场前景和开发方案H5小程序APP源码

随着移动互联网的快速发展和社交需求的日益增长&#xff0c;婚恋相亲交友软件小程序成为了越来越多单身人士的选择。本文将从市场前景、使用人群、盈利模式以及竞品分析等多个角度&#xff0c;综合论述这一领域的现状与发展趋势。 一、市场前景 在快节奏的现代生活中&#xf…

欢乐钓鱼大师加速、暴击内置脚本,直接安装

无需手机root,安装软件即可使用&#xff0c;仅限安卓。 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

顺序表链表经典算法题

1.链表反转 typedef struct ListNode listnode; struct ListNode* reverseList(struct ListNode* head) {if(head NULL){return head;}listnode* p1 NULL;listnode* p2 head;listnode* p3 head->next;while(p2){p2->next p1;p1 p2;p2 p3;if(p3)p3 p3->next;}…

[C++][算法基础]欧拉函数(常规求质数)

给定 n 个正整数 &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 1∼N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为 ϕ(N)。 若在算数基本定理中&#xff0c;N…&#xff0c;则&#xff1a; ϕ(N) N… 输入格式 第一行包含整数 n。 接下来 n 行&#xf…

21笔会计结转分录

21笔会计结转分录

基于机器学习的人脸发型推荐算法研究与应用实现

1.摘要 本文主要研究内容是开发一种发型推荐系统&#xff0c;旨在识别用户的面部形状&#xff0c;并根据此形状推荐最适合的发型。首先&#xff0c;收集具有各种面部形状的用户照片&#xff0c;并标记它们的脸型&#xff0c;如长形、圆形、椭圆形、心形或方形。接着构建一个面部…

客户端动态降级系统

本文字数&#xff1a;4576字 预计阅读时间&#xff1a;20分钟 01 背景 无论是iOS还是Android系统的设备&#xff0c;在线上运行时受硬件、网络环境、代码质量等多方面因素影响&#xff0c;可能会导致性能问题&#xff0c;这一类问题有些在开发阶段是发现不了的。如何在线上始终…

postgresql数据定时转存mongodb方案

案例背景很多事件记录在最初一段时间读写比较频繁,存储在postgresql比较合适,后期数据量变大,且仅作为历史记录查询,更适合存储在mongodb中,可能需要定期将postgresql中的数据转存到mongodb。案例分析postgresql数据定时转存mongodb,可以采用jdbc方式将postgresql读入内存…

node.js-模块化

定义&#xff1a;CommonJS模块是为Node.js打包Javascript代码的原始方式。Node.js还支持浏览器和其他Javascript运行时使用的ECMAScript模块标准。 在Node.js中&#xff0c;每个文件都被视为一个单独的模块。 概念&#xff1a;项目是由很多个模块文件组成的 好处&#xff1a…

模拟在页面点击导入csv

案例背景组件性能测试过程中,要导入大量自定义的数据。案例分析本案例中采用python的pandas库,模拟了生成导入csv文件,模拟在页面点击导入csv,使文件导入更高效。实现方案1****、在前端页面解析内部接口参数 典型的导入流程至少包含上传文件和确认上传。上传文件在浏览器中…

【Linux进阶之路】高级IO

一、 铺垫 I&#xff0c;即input为输入&#xff1b;O&#xff0c;即output为输出&#xff0c;IO&#xff0c;即input output为输入输出。IO一般是基于网卡&#xff0c;磁盘&#xff0c;光盘&#xff0c;U盘&#xff0c;磁盘&#xff0c;磁带等毫秒级别的外存&#xff0c;相较…