您的位置:首页 > 编程语言 > C语言/C++

c语言基础学习10_文件操作02

2018-01-28 11:57 225 查看
=============================================================================
=============================================================================
涉及到的知识点有:
六、stat函数
七、fread 和 fwrite函数
八、fopen的a模式说明
九、fopen的b模式说明
十、sftp传输文件时的说明
十一、fopen的其他模式简要说明
十二、实现二进制文件的拷贝
十三、fseek函数
十四、ftell函数
十五、fflush函数
十六、remove函数 和 rename函数 (文件删除函数和文件改名函数)
十七、通过fwrite将结构体保存到二进制文件中
课堂练习
=============================================================================
=============================================================================
六、stat函数

需要包含头文件有:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

功能:得到文件的各种属性(包括文件的属性、文件的建立时间、文件的大小等信息)。和文件的内容并没有关系。

int stat(const char *pathname, struct stat *buf);
函数的第一个参数:代表文件名。
函数的第二个参数:是struct stat结构。

一般头文件在"cd /usr/include"下面,这里是标准C程序头文件,如果你的头文件前加了 <sys/*>,那说明这是系统调用函数头文件,其在"cd /usr/include/sys"下面。
函数都是获取文件(普通文件、目录、管道、socket、字符、块)的属性。函数原型#include <sys/stat.h>。
--------------------------------------
linux下示例代码如下:

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

int main()
{
struct stat st = { 0 }; //该结构在头文件#include <sys/stat.h>里面已经定义过了,只需要去使用它就行了。
stat("a.txt", &st);     //调用完stat函数后,st里面就得到了该文件的属性。

int size = st.st_size;  //得到文件的大小。单位是:字节。
printf("%d\n", size);    //78

return 0;
}


文件对应的属性:
struct stat
{
mode_t     st_mode;      //文件对应的模式,文件,目录等。
ino_t      st_ino;      //inode节点号。
dev_t      st_dev;      //设备号码。
dev_t      st_rdev;     //特殊设备号码。
nlink_t    st_nlink;    //文件的连接数。
uid_t      st_uid;      //文件所有者。
gid_t      st_gid;      //文件所有者对应的组。
off_t      st_size;     //普通文件,对应的文件字节数。
time_t     st_atime;    //文件最后被访问的时间。
time_t     st_mtime;    //文件内容最后被修改的时间。
time_t     st_ctime;    //文件状态改变时间。
blksize_t  st_blksize;  //文件内容对应的块大小。
blkcnt_t   st_blocks;   //伟建内容对应的块数量。
};


=============================================================================
七、fread 和 fwrite函数

文本文件:就是我们能看的懂的文件。
二进制文件:就是我们看不懂的文件。

之前学的:fgets、fputs、fprintf、fscanf这些函数都是针对文本文件读写的,不能对一个二进制文件进行读写。
除了文本文件之外的文件都是二进制文件,比如图像、视频、音乐、可执行程序这些都是二进制的。
(其实文本文件也是二进制文件,文本文件是一种特殊的二进制文件,文本文件里面都是ASCII码。)

之前学习的内容都是往文件里面写一个字符串。
如果要把一个int整数直接写入文件,这个文件就不是文本文件了。
--------------------------------------
#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

第一个参数是buf的内存地址;
第二个参数是每个单位的大小;
第三个参数是写多少个单位;
第四个参数是fopen返回的文件指针。
返回值:返回的是实际写入的数据块数目。

fread的返回值是成功读取到的单位个数。
fread第二个参数代表了一个单位多大,第三个参数代表一次要读多少个单位。

功能:这两个函数以二进制形式对文件进行操作,不局限于文本文件。
--------------------------------------
linux下示例代码如下:

#include <stdio.h>

int main01()
{
FILE *p = fopen("a.dat", "w");

int a = 100;
fwrite(&a, 1, sizeof(int), p);  //要往文件里面写4个字节的内容。
//fwrite(&a, sizeof(int), 1, p);  //二者等价。
a = 123456789;                  //再往文件里面写4个字节的内容。
fwrite(&a, 1, sizeof(int), p);

fclose(p);

return 0;
}

int main()
{
FILE *p = fopen("a.dat", "r");

while (1)
{
//int a = 0;    //以int的方式打开。
//fread(&a, 1, sizeof(int), p);
unsigned char a = 0;    //以每个字节的方式打开。
fread(&a, 1, sizeof(char), p);
if (feof(p))
break;
//printf("%d\n", a);
printf("%x\n", a);  //以16进制的方式显示,小端对齐。
}
fclose(p);

return 0;
}
输出结果为:
64
0
0
0
15
cd
5b
7


-----------------------------------------------------------------------------
通过fread读取文件的每个字节。

linux下示例代码如下:

int main(int argc, char **args)
{
if (argc < 2)
return 0;

FILE *p = fopen(args[1], "r");

while (1)
{
//int a = 0;    //以int的方式打开。
//fread(&a, 1, sizeof(int), p);
unsigned char a = 0;    //以每个字节的方式打开。
fread(&a, 1, sizeof(char), p);
if (feof(p))
break;
//printf("%d\n", a);
printf("%x\n", a);  //小端对齐。
}
fclose(p);

return 0;
}


=============================================================================
八、fopen的a模式说明

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

  a    以附加的方式打开只写文件。若文件不存在,则会建立该文件。
      如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
  a+    以附加的方式打开可读写文件。若文件不存在,则会建立该文件。
      如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(原来的EOF符不保留)

  即:a的作用是:如果文件不存在,那么a和w是一样的;如果文件存在,那么不覆盖这个文件,而是往文件后面追加内容。
=============================================================================
九、fopen的b模式说明

  r     以只读方式打开文件,该文件必须存在,且文件必须是可读的。
  r+    以可读写方式打开文件,该文件必须存在。
  rb+   读写打开一个二进制文件,允许读写数据,文件必须存在。

  注意:b模式也只能在windows下去演示,在linux、unix和Android下无效。

windows所有的文本文件的每一行都是以\r\n结尾的,而不是以\n结尾的。
(即windows系统下的文本文件以\r\n来进行换行的。)
在windows下,如果读文件的时候,只加"r"参数,那么系统会自动把\n前面的\r吃掉,而一旦添加了参数b,那么系统就不会自动吃掉\n前面的\r了。
在windows下,如果写文件的时候,只加"w"参数,那么系统会自动在\n前面添加一个\r,而一旦添加了b参数,那么系统就不会自动在\n前面动添加\r了。

linux下所有的文本文件,每一行本来就是以\n结尾的,前面没有\r,参数b在linux下是无效的。

小规律:
在windows读写是一个二进制文件的时候,一般要加b,防止系统添加无谓的\r。
但如果读写的是一个文本文件,那么不要加b,这样可以不用单独处理这个\r了。

小结:
所以windows的文本文件要比linux的文本文件要大一些,因为windows的每一行会比linux的每一行多一个\r(即多1个字节)。
并没有为什么,这是当年微软在设计的时候,是从DOS过来的,那个时候DOS就是这样的。
所以有时候,把linux的文本文件拿到windows下打开的情况下,会发现不换行了。

即:windows和linux(unix)的文本文件是不一样的。
=============================================================================
十、sftp传输文件时的说明

如果是要用sftp去下载一个文本文件的时候:操作如下:
sftp> ascii
Using ascii mode to transfer files.
sftp> get file11.c
Downloading file11.c from /home/chen/001/01/文件操作day01/file11.c
100% 779 bytes 779 bytes/s 00:00:00
/home/chen/001/01/文件操作day01/file11.c: 779 bytes transferred in 0 seconds (779 bytes/s)
sftp>
--------------------------------------
如果是要用sftp去下载一个二进制文件的时候:操作如下:
sftp> binary
Using binary mode to transfer files.
sftp> get file11.c
Downloading file11.c from /home/chen/001/01/文件操作day01/file11.c
100% 779 bytes 779 bytes/s 00:00:00
/home/chen/001/01/文件操作day01/file11.c: 779 bytes transferred in 0 seconds (779 bytes/s)
sftp>

如下图所示:









=============================================================================
十一、fopen的其他模式简要说明
=============================================================================
十二、实现二进制文件的拷贝

windows下示例代码:

#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>

#define NUM 1024 * 64        //设置文件大小阈值为64K。

int main(int argc, char **args)
{
if (argc < 3)
return 0;

FILE *p = fopen(args[1], "rb");
if (p == NULL)
return 0;

FILE *p1 = fopen(args[2], "wb");
if (p1 == NULL)
return 0;

//先得到文件的大小。
struct stat st = { 0 };
stat(args[1], &st);
int size = st.st_size;

//判断阈值。
if (size >= NUM)
size = NUM;

//再根据文件的大小,动态分配一个堆内存出来。
char *buf = malloc(size);

while(!feof(p))
{
//char a = 0;
//fread(&a, 1, 1, p);                //一次读一个char。
//char a[1024] = { 0 };                //一次读一个1024个字节。
//fread(a, 1, sizeof(a), p);
//fwrite(a, 1, 1, p1);                //一次写一个char。
//fwrite(a, 1, sizeof(a), p1);        //一次写一个1024个字节。

int res = fread(buf, 1, size, p);    //把读的返回值传给要写的参数里面。
fwrite(buf, 1, res, p1);
}
fclose(p);
fclose(p1);
free(buf);

return 0;
}


=============================================================================
十三、fseek函数

  int fseek(FILE *stream, long offset, int whence);
  函数功能:设置文件指针stream的位置。
  如果执行成功,stream将指向以whence为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
  如果执行失败,则不改变stream指向的位置,函数返回一个非0值。

  第一个参数:stream为文件指针。
  第二个参数:offset为偏移量,整数表示正向偏移,负数表示负向偏移。
  第三个参数:whence为设定文件从哪里开始偏移,可能取值为:SEEK_SET、SEEK_CUR或SEEK_END。
    SEEK_SET:文件开头。
    SEEK_CUR:当前位置。
    SEEK_END:文件结尾。
  例如:fseek(fp, 3, SEEK_SET);

  实验得出:
  往前偏移超出文件末尾位置,还是返回0;
  往回偏移超出文件首位置,还是返回0。
  所以需要小心使用。

  其实FILE结构内部是有一个指针的,每次调用文件读写函数时,这些函数就会自动移动这个指针。
  默认情况下该指针只能从前往后移动。

windows下示例代码:

#include <stdio.h>

int main01()
{
FILE *p = fopen("a.dat", "w");

char a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
fwrite(a, 1, sizeof(a), p);
fclose(p);

return 0;
}

int main()
{
FILE *p = fopen("a.dat", "r");

fseek(p, 2, SEEK_SET);  //将文件指针从文件开始处位移2个字节。

int i;
for (i =0; i < 2; i++)
{
//char a = 0;
//fread(&a, 1, sizeof(a), p);

char a[2] = { 0 };
fread(a, 1, sizeof(a), p);

//fseek(p, -2, SEEK_CUR);      //将指针从当前位置回去2个字节。
//fseek(p, 0, SEEK_SET);      //将指针指向文件开始。
fseek(p, 0, SEEK_END);      //将指针指向文件结尾。
printf("%d, %d\n", a[0], a[1]);
}
fclose(p);

return 0;
}
输出结果为:
2, 3
0, 0


windows下示例代码:

#include <stdio.h>

//慢速生成超大文件的方法。
int main01()
{
FILE *p = fopen("a.dat", "w");

int i;
for (i = 0; i < 10000000; i++)
{
char a = 0;
fwrite(&a, 1, sizeof(a), p);
}
fclose(p);

return 0;
}

//利用fseek函数瞬间生成超大文件的方法。
int main()
{
FILE *p = fopen("a.dat", "w");

fseek(p, 10000000, SEEK_SET);   //将文件指针从文件开始处位移10000000个字节。

char a = 0;
fwrite(&a, 1, sizeof(a), p);
fclose(p);

return 0;
}


=============================================================================
十四、ftell函数

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

  long ftell(FILE *stream);
  例如:long len = ftell(fp);

可以通过fseek和ftell得到文件大小:
fseek(fp, 0, SEEK_END);
long len = ftell(fp);
--------------------------------------
windows下示例代码:

#include <stdio.h>

int main(int argc, char **args)
{
if (argc < 2)
return 0;

FILE *p = fopen(args[1], "r");
if (p == NULL)
return 0;

fseek(p, 0, SEEK_END);
long size = ftell(p);
printf("%ld\n", size);
fclose(p);

return 0;
}


=============================================================================
十五、fflush函数

  文本的读可以用:fgets和fscanf。
  文本的写可以用:fputs和fprintf。
  二进制文件读可以用:fread。
  二进制文件写可以用:fwrite。

其实c语言所有的文件操作函数都是缓冲区函数。 即都是针对缓冲区进行操作的。

文件读写缓冲区的说明如下图所示:




--------------------------------------
那么我们现在想绕过缓冲区,想直接将缓冲区的内容写入文件中(即磁盘)。该如何办呢?答:使用fflush函数。

  fflush函数可以将缓冲区中任何未写入的数据直接写入文件(即磁盘)中。
  函数执行成功则返回0,失败则返回EOF。

int fflush(FILE *stream);
  由于fflush是实时的将缓冲区的内容写入磁盘,所以不要大量的去使用该函数;(因为会使程序执行效率降低,而且会影响磁盘寿命。)
  但如果是特别敏感的数据,可以通过fflush写入磁盘(比如密码),防止由于电脑的各种故障,内存数据的丢失。
  (因为内存一断电就没有数据了,而磁盘断电数据还在,不会丢失。)
--------------------------------------
windows下示例代码:

//在缓冲区里面的代码:
#include <stdio.h>

int main()
{
FILE *p = fopen("a.txt", "w");

while (1)
{
char a[100] = { 0 };
scanf("%s", a);
if (strcmp(a, "exit") == 0)
break;
fprintf(p, "%s\n", a);
}
fclose(p);
return 0;
}
--------------------------------------
//绕过缓冲区里面的代码:
#include <stdio.h>

int main()
{
FILE *p = fopen("a.txt", "w");

while (1)
{
char a[100] = { 0 };
scanf("%s", a);
if (strcmp(a, "exit") == 0)
break;
fprintf(p, "%s\n", a);
fflush(p);    //把缓冲区的数据直接同步到磁盘。
}
fclose(p);
return 0;
}


=============================================================================
十六、remove函数 和 rename函数 (文件删除函数和文件改名函数)

  remove函数删除指定的文件。
  int remove(const char *pathname);
  参数pathname为指定要删除的文件名,如果是windows下文件名与路径可以用反斜杠\分隔,也可以用斜杠/分隔。

  rename函数将指定的文件改名。
  int rename(const char *OldFilename, const char *NewFilename);
=============================================================================
十七、通过fwrite将结构体保存到二进制文件中

linux下示例代码:

#include <stdio.h>
#include <string.h>

struct man
{
char name[20];
int age;
};
//向文件中写一个结构体。
int insert()
{
//struct man m = {"苍老师", 40};
//struct man m[3] = { {"苍老师", 40}, {"饭岛老师", 30}, {"陈冠希", 50} };
struct man m = { 0 };

FILE *p = fopen("a.dat", "a");  //a  以附加的方式打开只写文件。若文件不存在,则会建立该文件。
//如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
if (p == NULL)
return 0;
//fwrite(&m, 1, sizeof(struct man), p);
//fwrite(m, 3, sizeof(struct man), p);

while (1)
{
printf("请输入名字:");
scanf("%s", m.name);
if (strcmp(m.name, "exit") == 0)
break;
printf("请输入年龄:");
scanf("%d", &m.age);
fwrite(&m, 1, sizeof(struct man), p);
}
fclose(p);
return 0;
}
//向文件中读一个结构体。
int select()
{
//struct man m = { 0 };
//struct man m[3] = { 0 };
struct man m = { 0 };

FILE *p = fopen("a.dat", "r");
if (p ==NULL)
return 0;
//fread(&m, 1, sizeof(struct man), p);
//fread(m, 3, sizeof(struct man), p);

while (1)
{
fread(&m, 1, sizeof(struct man), p);
if (feof(p))
break;
printf("%s, %d\n", m.name, m.age);
}
/*
int i;
for (i = 0; i < 3; i++)
{
//printf("%s, %d\n", m.name, m.age);
printf("%s, %d\n", m[i].name, m[i].age);
}
*/
fclose(p);
return 0;
}
//参数1代表写入,参数2代表读出。
int main(int argc, int **args)
{
if (argc < 2)
return 0;
char c = args[1][0];
if (c == '1')
insert();
if (c == '2')
select();
return 0;
}


=============================================================================
课堂练习:
上面的代码功能是:只能添加姓名和年龄,显示的时候只能全部显示出来。
需要显示的时候,如果输入all,就全部显示,如果具体输入某一个人的名字,那么只显示这个人的名字和年龄。
如果输入一个不存在的人名,那么就显示not found。

linux下示例代码:

#include <stdio.h>
#include <string.h>

struct man
{
char name[20];
int age;
};
//向文件中写一个结构体。
int myinsert()
{
//struct man m = {"苍老师", 40};
//struct man m[3] = { {"苍老师", 40}, {"饭岛老师", 30}, {"陈冠希", 50} };
struct man m = { 0 };

FILE *p = fopen("a.dat", "a");  //a   以附加的方式打开只写文件。若文件不存在,则会建立该文件。
//如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
if (p == NULL)
return 0;
//fwrite(&m, 1, sizeof(struct man), p);
//fwrite(m, 3, sizeof(struct man), p);

while (1)
{
printf("请输入名字:");
scanf("%s", m.name);
if (strcmp(m.name, "exit") == 0)
break;
printf("请输入年龄:");
scanf("%d", &m.age);
fwrite(&m, 1, sizeof(struct man), p);
}
fclose(p);
return 0;
}
//向文件中读一个结构体。
int myselect()
{
//struct man m = { 0 };
//struct man m[3] = { 0 };
struct man m = { 0 };

char name[30] = { 0 };
printf("请输入名字:");
scanf("%s", name);

FILE *p = fopen("a.dat", "r");
if (p ==NULL)
return 0;
//fread(&m, 1, sizeof(struct man), p);
//fread(m, 3, sizeof(struct man), p);

int status = 0;
while (1)
{
fread(&m, 1, sizeof(struct man), p);
if (feof(p))
break;

if (strcmp(name, "all") == 0)
{
printf("%s, %d\n", m.name, m.age);
status = 1;
}
else
{
if (strcmp(name, m.name) == 0)
{
printf("%s, %d\n", m.name, m.age);
status = 1;
}
}
}
/*
int i;
for (i = 0; i < 3; i++)
{
//printf("%s, %d\n", m.name, m.age);
printf("%s, %d\n", m[i].name, m[i].age);
}
*/
fclose(p);
if (status == 0)
printf("没有找到!\n");
return 0;
}
//参数1代表写入,参数2代表读出。
int main(int argc, int **args)
{
if (argc < 2)
return 0;
char c = args[1][0];
if (c == '1')
myinsert();
if (c == '2')
myselect();
return 0;
}


=============================================================================
继续课堂练习:
可以删除一个指定的人名和对应的年龄,如果输入一个没有的名字,那么什么也不做,不能增加新的文件。

linux下示例代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct man
{
char name[20];
int age;
};
//向文件中写一个结构体。
int myinsert()
{
//struct man m = {"苍老师", 40};
//struct man m[3] = { {"苍老师", 40}, {"饭岛老师", 30}, {"陈冠希", 50} };
struct man m = { 0 };

FILE *p = fopen("a.dat", "a");  //a   以附加的方式打开只写文件。若文件不存在,则会建立该文件。
//如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
if (p == NULL)
return 0;

//fwrite(&m, 1, sizeof(struct man), p);
//fwrite(m, 3, sizeof(struct man), p);

while (1)
{
printf("请输入名字:");
scanf("%s", m.name);
if (strcmp(m.name, "exit") == 0)
break;
printf("请输入年龄:");
scanf("%d", &m.age);
fwrite(&m, 1, sizeof(struct man), p);
}
fclose(p);
return 0;
}
//向文件中读一个结构体。
int myselect()
{
//struct man m = { 0 };
//struct man m[3] = { 0 };
struct man m = { 0 };

char name[30] = { 0 };
printf("请输入名字:");
scanf("%s", name);

FILE *p = fopen("a.dat", "r");
if (p ==NULL)
return 0;

//fread(&m, 1, sizeof(struct man), p);
//fread(m, 3, sizeof(struct man), p);

int status = 0;
while (1)
{
fread(&m, 1, sizeof(struct man), p);
if (feof(p))
break;

if (strcmp(name, "all") == 0)
{
printf("%s, %d\n", m.name, m.age);
status = 1;
}
else
{
if (strcmp(name, m.name) == 0)
{
printf("%s, %d\n", m.name, m.age);
status = 1;
}
}
}
/*
int i;
for (i = 0; i < 3; i++)
{
//printf("%s, %d\n", m.name, m.age);
printf("%s, %d\n", m[i].name, m[i].age);
}
*/
fclose(p);
if (status == 0)
printf("没有找到!\n");
return 0;
}
//删除函数。
int mydelete()
{
FILE *p = fopen("a.dat", "r");
if (p == NULL)
return 0;

//得到文件的大小。
fseek(p, 0, SEEK_END);
int size = ftell(p);
//文件多大就设置一个多大的堆空间。
struct man *m = malloc(size);
//一下子就把文件的所有内容读入堆内容。
fseek(p, 0, SEEK_SET);
fread(m, 1, size, p);
fclose(p);
//输入要删除的人的名字。
printf("请输入要删除人的名字:");
char name[30] = { 0 };
scanf("%s", name);

//得到有多少条记录。
int n = size / sizeof(struct man);
p = fopen("a.dat", "w");
int i;
for (i = 0; i < n; i++)
{
if (strcmp(m[i].name, name) != 0)
fwrite(&m[i], 1, sizeof(struct man), p);
}
fclose(p);
free(m);
/*
//把所有的记录打印出来看一下。
int i;
for (i = 0; i < n; i++)
{
printf("%s, %d\n", m[i].name, m[i].age);
}
*/
return 0;
}
//参数1代表写入,参数2代表读出。参数3代表删除。
int main(int argc, int **args)
{
if (argc < 2)
return 0;
char c = args[1][0];
if (c == '1')
myinsert();
if (c == '2')
myselect();
if (c == '3')
mydelete();
return 0;
}


=============================================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐