欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于C语言中的文件操作

程序员文章站 2023-10-11 17:42:04
大一开了c语言课时,一直到最后我都是没有接触过文件操作的,那时马上要结课了,老师讲的也特别快,所以了解程度也不是太高,所以也觉得需要进行一下总结: 闲话也就不多说了,进入正题, 对于文件,我想概念不...

大一开了c语言课时,一直到最后我都是没有接触过文件操作的,那时马上要结课了,老师讲的也特别快,所以了解程度也不是太高,所以也觉得需要进行一下总结:

闲话也就不多说了,进入正题,
对于文件,我想概念不需要我们来多说了,在这里我们叙述一下c语言中对文件操作的流程:
首先,我们要打开文件,然后要读写文件,然后我们要关闭文件。

1.文件操作相关基础

关于流:
流是一个抽象出来的东西,流指的是一种信息转换,就是从外界接收或者接受数据。在这里要提一个叫做缓冲区的概念,缓冲区就是一个临时存储区,这个区进行数据的传输,完成高速部件和低速部件之间的数据传输。大部分流都是可以完全缓冲的。

接下来我们说一下所提供的至少三个流—标准输入流(stdin)、标准输出流(stdout)、标准错误(stderr)。这些都是一个指向file结构的一个指针。
对于file结构,就是我们所说的文件指针,这个指针可以指向一个文件,然后你就可以对这个文件进行后续的操作。

stdin:标准输入流,所说的就是输入的来源,一般 指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。

stdout:标准输出流,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。

stderr:一般指显示器:比如perror()等函数向stderr输出数据。

perror()函数:这个函数是用来简化向用户报告特定错误的过程。
函数原型:

void perror(char const *message);

这个函数如果message不是null并且指向一个非空的字符串,perror函数就打印出这个字符串,后面跟一个分号和一个空格。然后打印出一条当前错误代码的解释信息。
例如下面这个程序,我在这里没有这个文件,所以打开肯定会出错的。

#include
#include
int main()
{
    file *pread = fopen("test.txt", "r");
    if (pread == null)
    {
        perror("没有这个文件");
    }
    system("pause");
}

所以最后的输出是下面这样的。
关于C语言中的文件操作

2.打开文件与关闭文件

上面咱们已经说了要操作文件,首先要打开文件,所以接下来咱们对打开文件这个操作进行讲述一下。

打开文件:

fopen()函数

file *fopen(char *filename, char *mode);

这里的filename就是文件名,mode就是所说的访问方式。

这个函数,就是用来打开文件的函数。这个函数可以打开文件流,在这要注意,程序为必须同时处于活动状态的每一个文件声明一个指针变量,这个指针类型是file *,是一个文件指针。接下来对这个流进行操作,这时,你必须给出所要访问文件的访问方式,只有打开了这个文件,进行访问了,才能进行你接下来的常规操作,最后一定要记得使用fclose()函数关闭文件流。这样就可以防止一个流与和它相关联的文件再次被访问了,也保证了在缓冲区的数据都能被正确地写入文件。

比如我现在要对文件test.txt进行可写的操作状态。

file *pwrite=fopen("test.txt","w");

这里首先说一下访问方式:
文件打开方式由r、w、a、t、b、+ 六个字符拼成,各字符的含义是:
r:读
w:写
a:追加
t:文本文件
b:二进制文件
+:读和写
关于C语言中的文件操作

另外需要注意的是一般如果你打开失败的话,你的文件指针就会变成一个空指针,我们很多时候会利用这个来看文件是否打开成功。当然,如果打开失败,它返回的就是一个file结构的地址。

关闭文件:

fclose()函数

fclose(file *f);

关闭文件流,这个函数在文件关闭之前会刷新缓冲区。如果它执行成功,那么fclose返回零值,如果失败,返回eof。

3.关于字符

关于字符i/o,在我们进行文件操作,最简单的就是进行字符的操作,字符操作会牵扯一些函数,我们来看一下这些函数。

getchar()函数家族

int fgetc(file *stream);
int getc(file *stream);
int getchar(void);

首先明确,这些函数都是用来读取字符,他们的返回值都是int,为什么不返回char类型呢,主要是为了允许函数报告文件结尾。
这三个函数的区别是getchar函数始终都是从标准输入流中进行读取。fgetc函数和getc函数都是从文件流中读取。

putchar()函数家族

int fputc(int character,file *stream);
int putc(int character,file *stream);
int putchar(int character);

前两个函数进行对文件流操作,第一个参数是要被打印的字符。打印之前,整形参数会转化为无符号整形字符型。putchar是对标准输出流操作,只打印一个字符。

ungetc()函数

int ungetc(int character,file *stream);

这个函数是用来把先前读入的字符返回到流中,这样它可以在以后重新被读入。

4.关于字符串

使用字符读取进行文件操作,效率太低,所以我们也就不得不考虑使用字符串的操作。

gets()函数家族

char *fgets(char *buffer,int buffer_size,file *stream);
char *gets(char *buffer);

buffer指的是你要读取字符以后保存的缓冲区,这个缓冲区你自己来设定,比如一个字符数组。buffer_size指的是所读取的个数,最后是你要读取的文件流。
注意,fgets读取到的字符串会在末尾自动添加 ‘\0’,n 个字符也包括 ‘\0’。也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,buffer_size的值应该为 101。所以fgets无法把字符串读入到一个长度小于两个字符的缓冲区。
而gets函数他没有设置缓冲区长度参数,这样会有一个大问题,如果当一个长输入行读到一个很短的缓冲区的时候,多出来的字符后写到这块缓冲区后面的内存空间上,然后这个时候,就会产生问题,你原本保留在后面那块内存空间上的内容就被破坏了。所以在使用gets函数的时候,大家应该注意,要设置一个比较大的缓冲区,或者因为gets函数很危险(容易造成系统),最好不要用

fgets() 遇到换行时,会将换行符一并读取到当前字符串。而 gets() 不一样,它会忽略换行符。

puts()函数家族

int fputs(char const *buffer,file *stream);
int puts(char const *buffer);

fputs() 函数可以用来向指定的文件写入一个字符串,这个字符串最后要以nul结尾,所以这个函数也就没有缓冲区长度参数。成功写入一个字符串后,文件的位置指针会自动后移,函数返回值为非负整数;否则返回eof(符号常量,其值为-1)。

注意:gets()丢掉输入里的换行符,但puts()为输出添加换行符。另一方面,fgets()存储输入中的换行符,而fputs()也不为输出添加换行符。

5.关于格式化读写文件

scanf()函数家族

int fscanf(file *stream,char const *format,...);
int sscanf(char const *string,char const *format,...);
int scanf(char const *format);

这些函数都是从输入源读取赐福然后进行格式的转化。fscanf可用于所有的流,scanf从标准输入流读取,sscanf则从第一个参数所给出的字符串中读取字符。
fscanf() 返回参数列表中被成功赋值的参数个数
这里sscanf会将string里面字符串转化成你想要的格式。
如下:


  sscanf("123456 ", "%4s", buf);
  printf("%s\n", buf);
 

上面的程序,对字符串去了长度为4的内容,所以最后输出是1234

printf()函数家族

int fprintf(file *stream,char const *format...);
int printf(char const *format);
int sprintf(char *buffer,char const *format);

这里可以对比上面的,printf处理的是标准输出,fprintf可以处理所有的流,而sprintf处理的是内存中的字符串。和sscanf类似,sprintf函数可以把buffer中的内容转换成字符串输出。
fprintf() 返回成功写入的字符的个数,失败则返回负数。

6.二进制形式文件操作

对于文件操作当中,最有效率的方式是二进制形式操作了,因为机器传输过程中,也是通过二进制,所以这样就不用再次进行数值转换了。

fread()函数

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

fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。

fwrite()函数家族

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

fwrite() 函数用来向文件中写入块数据。

7.刷新和定位函数

首先对于刷新函数,我们一般采用的就是fflush函数,这个函数一般使用它来刷新缓冲区,其他关于对这个缓冲区的问题,我在前期的一篇文章中已经讨论了这个问题。c语言之清空缓存区

int fflush(file *stream);

接下来所说的是一些定位函数,fseek函数。

int fseek(file *stream,long offset,int from);

fseek函数允许你在一个流当中进行定位,这个操作将改变下一个读取或者写入操作的位置。
fp 为文件指针,也就是被移动的文件。

offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。

from 为起始位置,也就是从何处开始计算偏移量。c语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:

seek_set 文件开头
seek_cur 当前位置
seek_end 文件结尾

void rewind(file *stream);

rewind函数将读/写指针设置回指定流的起始位置。

int fgetpos(file *stream,fpos_t *position);
int fsetpos(file *stream,fpos_t const *position);

fgetpos函数进行在pos所指的位置放置一个fpos_t值,这个值描述了文件中的一个位置。

fsetpos将文件指针定位在pos指定的位置上。该函数的功能与前面提到的fgetpos相反,是将文件指针fp按照pos指定的位置在文件中定位。

8.文件操作函数

这里我们简单说下两个函数,这两个函数,不进行输入输出,只用来操作文件,他们成功返回零值,失败,都返回非零。

remove函数

int remove(char const *filename);
int rename(char const *oldname,char const *newname);

remove 函数删除一个指定的文件。
rename,对文件名字进行改变,从oldname变为newname。