c语言 — 文件操作【求个关注!】


1 为什么要使用文件?

如果没有文件,我们使用的数据是储存在电脑的内存中的,内存一回收,
我们的数据即丢失,为了能*持久化*的保存数据,我们采用文件

2 什么是文件?

硬盘(磁盘)上的文件是文件,但在程序设计中,
可分为两种文件—程序文件与数据文件(从文件功能的角度进行分析)

2.1 程序文件

程序文件包括源文件(后缀为.c)    目标文件(windows环境下后缀为 .obj )
可执行文件(windows环境下为.exe)

2.2 数据文件

文件中不一定存放执行的程序,也可以存放程序所需的数据,存放数据的文件
我们称为数据文件
数据文件包括文本文件与二进制文件
 数据在内存中是以二进制的形式进行存储,如果不加转换地移入外存的文件
 中,此文件即为二进制文件
 如果要求在外存上以ASCII码的形式进行存储,则数据在存储前转换格式,此文件则
 被称为文本文件
 针对数据:字符在文件中一律以ASCII码值的形式存储,而数值型数据以
 二进制形式或者ASCII码值的形式存储

举例:

#include<stdio.h>
int main() {
	//打开文件
	FILE* p = fopen("test6.txt", "wb");//wb是以二进制的形式写入文件
	//将数据以二进制形式输入文件中去
	int a = 10000; 
	//功能是将10000以二进制的形式输入文件
	fwrite(&a, 4, 1, p);
	//关闭文件
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
对于数据输入到文本文件中去的例子,关于读写函数时会涉及到!

2.3 关于文件名问题!

一个文件中只有一个对应的文件标识 - 便于用户引用
文件名由
文件路径 + 文件名主干 + 文件后缀
例如:
C:\Users\26921\Desktop\专业\test.txt
在test之前,是文件路径
test是文件名主干
.txt是文件后缀

2.4 扩展

小扩展:计算机硬件设备是文件得以存储数据的基础
       计算机的存储分为内存与外部存储器
      内存是cpu存放中间数据和与外部存储器交换的信息的地方
      而外部存储器则存放系统程序与用户程序及一些常用的数据!
      c盘与d盘皆是外部存储器

3 关于流的思想

程序执行的数据的来源可以是键盘,光盘,磁盘,u盘等等
程序输出的设备也可以是各种不同类型的输出设备,抽象掉这些无关紧要的
因素,我们可以看到,数据像一个水流一样流入程序,又流出程序,所以我
们引入了流的思想

3.1 标准流

 stdin标准输入流 ——一般从键盘中输入数据,scanf函数就是从标准输入流
 中获取数据
 stdout 标准输出流—— 大多数环境输出至显示器页面,printf函数就是将
 数据输出到标准输出流中
 stderr 标准错误流——大多数环境将其输出到显示器页面
 stdin,stdout,stderr的类型均是FILE * 通常称为文件指针,
 c语言中是通过FILE * 的文件指针,来维护对流的各种操作的

4 文件信息区

对于每一个被打开一个文件时,系统会在内存中开辟一个关于此文件的文件信息区
保存此文件的信息(比如此文件的名字,地址,文件的状态等等)
系统事先设定的结构体名称为 FILE

如图:
在这里插入图片描述
我们通过文件信息区,间接地对文件进行操作!

5 fopen函数与fclose函数

当我们要对文件进行操作时,要先打开文件,然后进行操作,操作文件完后,再关闭文件
而打开与关闭文件的功能由fopen 与fclose函数实现

5.1 fopen函数

fopen 函数用于打开文件
fopen 的格式:
   FILE* fopen(const char* FILEName,const char* Mode)
//  第一个参数是文件的地址 , 第二个参数是对文件操作的类别
// 此函数返回文件信息区的地址,我们通过此地址间接地对文件中的内容
进行操作。

操作类别:
在这里插入图片描述
注意:

1   读:是指程序读取文件中的数据,写:是指程序写入数据到文件中
2   要注意当我们将字符串赋给形参时,本质上是将首元素的地址赋给形参,所以在填写操作类别时,
要用" " 而不是' '  ,前者是字符串的格式,后者是字符的格式

举例:
在这里插入图片描述
新建一个文本文档:
新建一个文本文档
在这里插入图片描述


#include<stdio.h> //文件操作函数包含在stdio文件之中!
int main(){
    // 打开文件text并返回文件信息区的地址
	FILE* p1 = fopen("text.txt", "r");//执行只读操作
	return 0;
   }

5.2 fclose函数

fclose函数用于关闭文件
int fclose(FILE * Stream);
函数的参数是文件信息区的地址
#include<stdio.h> //文件操作函数包含在stdio文件之中!
int main(){

	FILE* p1 = fopen("text.txt", "r");
	fclose(p1);
	return 0;
   }

6 顺序读写函数的介绍

在打开文件之后就需要对文件进行读写:下面讲一下这些读写函数:
这些读写函数中有些能用作文件流的操作,有些能用作所有的输入流或输出流,故将这些函数命名为文件操作函数是不合理的!
在这里插入图片描述

注意:输入:将文件中的数据输入到程序中
      输出:通过程序将数据存放到文件中

6.1 fgetc函数

fgetc函数的功能是一个字符一个字符地读取数据。
格式:int fgetc(FILE* Stream)
fgetc的参数是对应的流的地址,返回值是int 类型,当读取成功时返回读取字符的ASCII码值,
当什么都没读取到时,返回EOF ,EOF是-1

举例:
在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
	FILE* p = fopen("text.txt", "r");//打开文件,只读操作 与fgetc函数相匹配
	assert(p);
	int a = fgetc(p);
	printf("%c\n", a);
	fclose(p);
	return 0;
}

在这里插入图片描述
出现这种结果的原因是文件名输入错误,对于只读操作,如果打开的文件不存在则报错。

#include<stdio.h>
#include<assert.h>
int main() {
	FILE* p = fopen("test.txt", "r");//此时将地址改正
	assert(p);
	for (int i = 0; i < 3; i++) {
	int a = fgetc(p);  //三次读取
	printf("%c ", a);
}
	fclose(p);
	return 0;
}

在这里插入图片描述

 结果说明了 :当我们重复引用fgetc函数时,其在文件中的光标不断的往后移动,
 因此我们可以不断地读取新的字符,其他的函数是否也是同样的原理?

6.2 fputc函数

fputc函数用于从程序中输出单个字符到文件中
格式:int fputc(int Character,FILE*Stream)
第一个形参是要输入的字符,第二个形参是目标文件信息区地址

在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
	//fputc函数
	//格式:int fputc(int Character,FILE*Stream)
	//第一个形参是要输入的字符,第二个形参是目标文件信息区地址
	FILE* p = fopen("test.txt", "w");//打开文件,只写操作 可以与fputc函数相匹配
	for (int i = 0; i < 10; i++) {
		fputc('a', "test.txt");
	}
	fclose(p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

6.3 fgets 函数

fgets函数的功能是从文件中获取一串字符
fgets函数的格式:char * fgets(char * str,int num,FILE *Stream)
第一个参数是读取的数据所存放到的空间,第二个参数是最多读取num个字符
(但是最后一个字符会自动添加'\0',所以最多从文件中读取num-1个字符)
第三个参数是文件信息区的地址
如果fgets函数读取失败会返回NULL指针。
情况1:

在这里插入图片描述

#include<stdio.h>
int main(){
  	FILE* p = fopen("test.txt", "r");//打开文件,只读操作,与fgets函数相匹配
	char arr[10] = { "xxxxxxxxxx" };
	fgets(arr, 10, p);
	for (int i = 0; i < 10; i++) {
		printf("%c ", arr[i]);
	}
	fclose(p);
	return 0;
}

在这里插入图片描述
结果显示为9个字符
监视:
在这里插入图片描述

情况2:

在这里插入图片描述

#include<stdio.h>
int main(){
  	FILE* p = fopen("test.txt", "r");//打开文件,只读操作,与fgets函数相匹配
	char arr[10] = { "xxxxxxxxxx"};
	fgets(arr, 10, p);
	for (int i = 0; i < 10; i++) {
		printf("%c", arr[i]);
	}
	fclose(p);
	
	return 0;
}

结果显示:
在这里插入图片描述

  这说明此函数在读取文件数据时,只会一行一行的读取,而不会换行读,
  即使读取的字符不足num个,打印的数据换行说明读取到了换行符,之后
  再加上'\0',如果依然不满足个数,则不再读取

在这里插入图片描述

6.4 fputs函数

fputs函数的功能在于将一串字符输入到文件中;
格式:int fputs(const char*Buffer , FILE* Stream)
第一个参数是要输入文件的字符串的首地址,第二个参数是文件信息区的地址

在这里插入图片描述

#include<stdio.h>
int main() {
 	FILE* p = fopen("test.txt", "w");//打开文件,只写操作,
 	                                 //与fputs函数相匹配
	fputs("abcdef", p);
	fclose(p);
	return 0; 
}

在这里插入图片描述
在这里插入图片描述

6.5 对比两组函数

scanf/fscanf
printf/fprintf

(1)scanf与fscanf函数

在这里插入图片描述
在这里插入图片描述

对比这两个函数我们发现,在形式上:fscanf 比 scanf多一个参数,在功能上:scanf是能键盘上即标准输入流中读取格式化数据,而fscanf可以在任意流中读取格式化数据,比如文件流。【既然是格式化数据,就需要占位符!】

#include<stdio.h>
#include<assert.h>
int main() {
	char a = 0;
	//使用scanf函数,从标准输入流中读取数据
	scanf("%d", &a);
	printf("%d\n", a);
    return 0 ;

在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
    char a = 0 ;
	//使用fscanf函数从标准输入流中读取数据
	fscanf(stdin,"%c", &a);
	printf("%c\n", a);

	return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
//使用fscanf函数从文件流中读取数据
	FILE* p = fopen("test.txt", "r");
	assert(p);
	fscanf(p, "%c", &a);
	printf("%c\n", a);
	fclose(p);

	return 0;
}

在这里插入图片描述
在这里插入图片描述

// 在数组中的数据能不能算一个流?
	char a = 0;
	char arr[10] = { "abcde" };
	fscanf(arr, "%c", &a);
	printf("%c\n", a);

在这里插入图片描述
结果显示报错!

(2)printf函数与fprintf函数

在这里插入图片描述
在这里插入图片描述

对比两个函数,在格式上:fprintf函数比printf多一个FILE* stream的参数
在功能上:printf函数是将格式化的数据输出到标准输出流上,
而fprintf函数可以将格式化的数据写到流上!【既然是格式化数据,就需要占位符!】

#include<stdio.h>
#include<assert.h>
int main() {
	char a = 'a';
	printf("%c\n", a);
	return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
	
	//采用fprintf函数向标准输出流输出数据
	char a = 'b';
	fprintf(stdout, "%c\n", a);
	return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<assert.h>
int main() {
	//采用fprintf函数向文件流写入数据
	char a = 'b';
	FILE* p = fopen("test.txt", "w");
	assert(p);
	fprintf(p, "%c", a);
	fclose(p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3)sscanf函数与sprintf函数

在这里插入图片描述
sscanf的功能即从字符数组中得到数据并格式化地赋值到指定的变量中去:
代码:

#include<stdio.h>
#include<assert.h>
struct MyStruct
{
	char name[10];
	int age;
	float i;

};

int main() {
	//
	char arr[100] = { "张三 18 65.5f" };
	struct MyStruct s1 ;
	sscanf(arr, "%s %d %f",s1.name,&(s1.age),&(s1.i));
	printf("1 将数据以格式化的形式打印 :%s %d %f\n", s1.name, 
	s1.age, s1.i);
	return 0;
}

在这里插入图片描述

sprintf函数:
在这里插入图片描述
sprintf函数的功能是将得到的数据以格式化的形式储存至字符数组中,本质是将得到的数据以字符串的形式存储起来
代码:

struct MyStruct
{
	char name[10];
	int age;
	float i;

};
#include<stdio.h>
int main() {
	char arr[50] = { 0 };
	struct MyStruct s2 = { "张三",18,23.6f };
	sprintf(arr, " %s %d %f", s2.name,s2.age,s2.i);
	printf("%s\n", arr);
	return 0;
}

在这里插入图片描述

6.6 fread与fwrite函数

(1)fread函数

在这里插入图片描述

此函数的几个参数是 void类型的指针 ,元素的大小,元素的个数,可访问到文件流的地址
fread函数的功能是从文件流中读取count个size大小的元素到ptr所指向的空间中去!
fread函数是二进制输入!

#include<stdio.h>
#include<assert.h>
int main() {
	//fread函数
	char a[10] = { 0 };
	FILE* p = fopen("test.txt", "rb");//虽然test.txt是文本文件,而rb是对二进制文件进行读取
	//,但是依然可以读取文件中的数据,应是以二进制的形式进行读取
	assert(p);
	fread(a, 1, 10, p);
	for (int i = 0; i < 10; i++) {
		printf("%c\n", a[i]);
	}
	fclose(p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
本结果说明,fread函数不会自动为字符串添加’\0’

(2)fwrite函数

在这里插入图片描述
本函数的参数与上一个参数大体相同,功能是将从ptr指向空间中,count个size大小的元素写入到文件流中去,是以二进制的形式写入!

#include<stdio.h>
#include<assert.h>
int main() {
	//fwrite
	FILE* p = fopen("test.txt", "wb");//以二进制的方式写入文件
	assert(p);
	char arr[10] = { "abcdefghi" };
	fwrite(arr, 1, 10, p);
	fclose(p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因为写入的格式是二进制,所以在文本文件中显示为乱码!

7 文件的随机读写

在前面的读写函数中,读与写的操作的位置是从文件的起始地址顺序往后的,
而当我们需要读写数据在文件中特定位置时,该怎么办呢?

7.1 fseek

在这里插入图片描述

形参origin是指针的位置:

在这里插入图片描述

如图:SEEK_SET是文件的起始地址
      SEEK_CUR是文件指针当前所处的地址
      SEEK_END是文件末尾的地址

本函数的功能是使文件指针指向 origin位置的偏移量处
要指向的字符在起始位置的左边,则偏移量>0,在左边,则偏移量<0;
代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
int main() {
	FILE* p = fopen("test4.txt", "r");
	assert(p);
	char c = fgetc(p);
	printf("%c\n", c);
	fseek(p, 4, SEEK_CUR); //以当前指针位置为起始位置,做偏移量!
	c = fgetc(p);
	printf("%c\n", c);
  fclose(p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

7.2 ftell

long int ftell(FILE * Stream)

本函数的功能是返回文件指针相对于文件指针原始位置的偏移量

	//如果新建一个文本文件则解决了这个问题
	FILE* p = fopen("test4.txt", "r");
	assert(p);
	char c = fgetc(p);
	printf("%c\n", c);
	fseek(p, 4, SEEK_CUR);
	c = fgetc(p);
	printf("%c\n", c);
	 int a = ftell(p);
	 printf("%d\n", a);

   fclose(p);
	

在这里插入图片描述

在这里插入图片描述

7.3 rewind

本函数的功能:使文件指针返回原始位置

void rewind (FILE * Stream)
#include<stdio.h>
int main(){
FILE* p = fopen("test4.txt", "r");
assert(p);
char c = fgetc(p);
printf("%c\n", c);
fseek(p, 4, SEEK_CUR);
c = fgetc(p);
printf("%c\n", c);
//计算此时文件指针位置对于原始位置的偏移量
 int a = ftell(p);
 printf("%d\n", a);
//将文件指针返回原始位置
 rewind(p);
// 测试:看其是否回到原始位置:
 c = fgetc(p);
 printf("原始位置的字符:%c\n", c);
fclose(p);
p = NULL;
return 0;
}

在这里插入图片描述

8 文件读取结束的判定

8.1 判断文件是否读取结束

1 在文本文件中判断文件读取结束的标志,看其返回值是否为EOF(fgetc)或者NULL(fgets)
2 在二进制文件中判断文件读取结束的标志是,返回值小于要读取字符的个数:
例:fread函数的返回值小于要读取元素的个数。

8.2 feof函数

文件读取结束的原因有两种:一种是读到文件末尾结束,
另一种是遇到错误停止读取.

我们用feof函数判断是否是因为读到文件末尾而结束
用ferror函数判断是否是因为遇到错误而停止读取

打开的一个流时,这个流上有两个标记值  
一个标记值是是否遇到文件末尾
一个标记值是是否读取遇到错误
遇到哪种情况,哪种情况的标记值则被设置
我们用相应的函数去判断其标记值是否被设置

feof:
在这里插入图片描述
在这里插入图片描述
代码:

#include<stdio.h>
int main(){
	FILE* p = fopen("test5.txt", "r");
	if (p == NULL) {
		perror("p");
		return 1;
	}
	while ((fgetc(p)) != EOF) {
		char ch = fgetc(p);
	}
	//如果是遇到文件末尾结束, feof判断相应的标记值则返回不等于0的数
	if (feof(p)) {
		printf("是因为遇到文件末尾结束!");
	}
	//如果是因为读取发生错误,导致文件结束
	else  if (ferror(p)) {
		printf("是因为读取错误,读取结束!");

	}
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

9 文件缓冲区

ANSIC采用文件缓冲系统处理数据文件
文件缓冲系统指在内存中为程序中正在使用的文件开辟一块文件缓冲区,
程序在向硬盘中输入数据时,先将数据存放到缓冲区中,装满缓冲区后,
再送到硬盘上。
硬盘向计算机中读入数据,也是先将数据存放到缓冲区中,然后再从缓冲区移
交至程序。
缓冲区的大小由c编译系统决定!

在这里插入图片描述

fclose函数是将缓冲区刷新至硬盘的一个操作!
  • 43
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Linux是一款兼容UNIX的操作系统,它提供了广泛的开发和应用环境。在许多嵌入式应用、通信设备和实时控制应用中,串口通讯被广泛应用。本文将介绍如何使用C语言在Linux中操作串口进行传输数据的相关知识。 串口是计算机中重要的外设之一,它是一种通过RS-232标准接口进行数据传输的设备。在Linux中,可以使用tty设备来表示串口。串口的读写操作,可以通过使用Linux系统提供的设备驱动程序进行实现。 对于通过串口进行通讯的应用场景来说,需要使用C语言来控制串口的打开、关闭、读写等操作。下面就来介绍一下如何使用C语言进行串口编程。 首先,要使用fopen()函数打开串口设备文件,代码如下: ``` #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> int main() { int fd; fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY); if(fd == -1) { printf("Error: cannot open serial port.\n"); exit(0); } /* Other operations */ close(fd); return 0; } ``` 在这个例子中,"/dev/ttyUSB0"代表的是一个USB口设备。O_RDONLY或O_WRONLY为文件打开时的读写权限,O_NOCTTY表示不把串口作为控制终端,O_NDELAY表示无阻塞方式。 接下来需要对串口进行配置。配置的内容有:波特率、数据位、停止位等。这些配置通过termios结构体进行配置。代码如下: ``` struct termios opt; tcgetattr(fd, &opt); cfsetispeed(&opt, B9600); cfsetospeed(&opt, B9600); opt.c_cflag &= ~CSIZE; opt.c_cflag |= CS8; opt.c_cflag &= ~CSTOPB; opt.c_cflag &= ~(PARENB | PARODD); opt.c_cflag |= IGNPAR; opt.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG); opt.c_oflag &= ~OPOST; opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); opt.c_cc[VMIN] = 1; opt.c_cc[VTIME] = 0; tcsetattr(fd, TCSANOW, &opt); } ``` 其中,cfsetispeed()和cfsetospeed()用于设置输入输出波特率,CSIZE也就是数据位有五个选项,分别是CS5、CS6、CS7、CS8、CS16,这里选择了CS8。CSTOPB代表停止位有一个或两个,这里选择了一个。PARENB和PARODD代表是否开启奇偶校验位,这里选择了不开。IGNPAR为忽略校验错。ICANON、ECHO、ECHOE、ISIG等代表输入方式、输出方式和控制模式。VTIME和VMIN代表在无数据到来的情况下读取的最大等待时间和最小字符数。 若要在串口中传送数据,需要使用write()函数进行写入操作。代码如下: ``` write(fd, buf, len); ``` 其中,fd为串口设备文件描述符,buf为写入缓存区的数据,len为数据长度。如果要读取数据,则需要使用read()函数。代码如下: ``` read(fd, buf, len); ``` 当从串口读取数据时,这个函数会一直阻塞,直到读到足够的数据或者时间超时。同样地,fd为串口设备文件描述符,buf为存放读取数据的缓冲区,len为读取数据的字节数。 最后,要关闭串口设备文件,使用close()函数就可以了。代码如下: ``` close(fd); ``` 在本文中,我们介绍了如何使用C语言在Linux中操作串口进行数据传输。针对不同的串口设备,我们需要根据不同的设备文件进行调整。串口编程本质上是使用Linux系统提供的设备文件读写操作函数进行控制。如果您需要深入学习Linux中串口编程的相关技术,请关注Linux内核驱动开发等相关方面的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值