嵌入式软件开发 day14(标准IO 文件IO)

APUE

IO:

标准io(std)(5)

文件io(sys)(3)

高级io(adv)(14)(阻塞的,非阻塞的)

并发:

多进程+信号(10,8)

多线程(11,12)

IPC:

PIPE管道(15)

XSI:msg,sem,shm(15)

SOCKET(套接字跨网络的,不在一台主机上的):dgram,stream(16)

杂:4,6,7,9,13

嵌入式软件开发 day14(标准IO 文件IO)_第1张图片

标准IO

IO:inpute & output 是一切实现的基础

stdio=>标准io 贯穿始终的类型:FILE* <- fopen()

常见标准IO函数
  • fopen

  • fclose

  • fread

  • fwrite

  • fseek

  • fflush

    强制把IO缓存区的数据写入到页缓存区

文件IO五大模式
  • 阻塞模式
  • 非阻塞模式
  • IO多路复用
  • 异步IO
  • 信号驱动IO

嵌入式软件开发 day14(标准IO 文件IO)_第2张图片

标准IO移植性比较好,可以在不同内核下的平台上使用

标准化函数依赖于系统调用

一、fopen() & fclose()

1.1 fopen()

描述:使用给定的模式 mode 打开 pathname 所指向的文件(打开文件)

头文件

#include 

函数原型

FILE *fopen(const char *pathname, const char *mode);
//FILE *fdopen(int fd, const char *mode);
//FILE *freopen(const char *pathname, const char *mode, FILE *stream);
  • pathname – 文件路径
  • mode – 打开模式

用法

FILE *fp;
fp = fopen("tmp.log","w");
fp = fopen("/home/user/tmp.log","w");

模式

参数 含义
r 以只读形式打开文件,流被被定位到文件起始位置
r+ 以读和写形式打开文件,流被定位到文件起始位置
w 清空文件或创建文件去写,流被定位到文件起始位置
w+ 以读和写的形式打开文件,文件有则清空,无则创建,流被定位到文件起始位置
a 以写入文件末尾的方式打开文件,流被定位到文件末尾位置
a+ 以读的方式,流被定位到文件起始位置,以写的方式,流被定位到文件末尾位置

注意:

r/r+:要读的文件必须是已经存在的

以传入的字符串开头为模式,不管字符串后边是什么

例:"rwa+"	=>	以r开头	=>	只读
	"read"	  =>	以r开头	=>	只读

文件末尾的位置,指针指向的是文件中最后一个元素下一个位置的地址空间

返回值

成功:返回FILE 指针

失败:空指针

返回值为指针类型:存放在堆或静态区上,不存放在栈上

注意

不可能存放在栈上,栈存放局部变量,局部变量的生命周期只在函数内有效

存放在静态区时要在一个操作完全执行完再进行另一个操作,否则会覆盖,丢失数据

存放在堆上会有内存的申请和释放

1.1.1 errno

描述:通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误,涵盖了系统所有可能的出错情况

头文件

#include 

存放路径

/usr/include/asm-generic/errno.h
/usr/include/asm-generic/errno-base.h

1.1.2 perror

描述:把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 s,后跟一个冒号,然后是一个空格。只会输出最近一次的错误提示

头文件

#include 
#include 

函数原型

void perror(const char *s);

用法

perror("fopen()");
1.1.3 strerror

描述:打印errno的错误号所描述的内容

头文件

#include 

函数原型

char *strerror(int errnum);

用法

printf("fopen():%s\n",strerror(errno));
1.1.4 命令:ulimit
参数 含义
-a 显示全部命令
-n 更改打开文件最大数量
open file	(-n) 1024
ulimit -n 2000
open file	(-n) 2000

拓展

用man手册查看:setlimit,setrlimit

在系统创建进程中,建进程时的三个默认的文件描述符(stdin(标准输入) stdout(标准输出) stderr(标准出错)) Linux进程默认情况下会有3个缺省打开的文件描述符
三个进程都是FILE *类型。

1.2 fclose()

描述:关闭文件

头文件

#include 

函数原型

int fclose(FILE *stream);

用法

fclose(fp);

返回值

成功:返回0

失败:返回EOF(系统宏定义为-1)

1.3 代码演示

#include 
#include 
#include 
#include 

int main()
{
	FILE *fp;

	fp = fopen("tmp.log","w");
//	fp = fopen("/etc/shadow","r");
	if(fp == NULL)
	{
//		printf("fopen failed.errno = %d\n",errno);
//		perror("fopen()");
		printf("fopen():%s\n",strerror(errno));
		exit(1);
	}

	puts("OK!");

	fclose(fp);

	exit(0);
}

二、fgetc() & fputc()

文件的位置指针像眼睛一样,顺次向后操作,读完这个位置,移动到下个位置

2.1 fgetc()

描述:从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动 (从文件读一个字符)

头文件

#include 

函数原型

int fgetc(FILE *stream);

返回值

成功:该函数以无符号 char 强制转换为 int 的形式返回读取的字符

失败:返回EOF(系统宏定义为-1)

2.2 fputc()

描述:把一个指定的字符,输出到目标文件中去 (往文件写一个字符)

头文件

#include 

函数原型

int fputc(int c, FILE *stream);

返回值

成功:返回非负数

失败:如果到达文件末尾或发生读错误,则返回 EOF(-1)。

2.3 代码演示

将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以单个字符为单位进行文件拷贝

#include 
#include 
#include 
#include 


int main(int argc,char *argv[])
{
	FILE *fps,*fpd;
	int ch;

	if(argc < 3)
	{
		fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
		exit(1);
	}

	fps = fopen(argv[1],"r");
	if(fps == NULL)
	{
		perror("fopen()");
		exit(1);
	}

	fpd = fopen(argv[2],"w");
	if(fpd == NULL)
	{
		fclose(fps);
		perror("fopen()");
		exit(1);
	}

	while((ch = fgetc(fps)) != EOF)
		fputc(ch,fpd);

	fclose(fpd);
	fclose(fps);
	exit(0);
}

三、fscanf() & fprintf()

3.1 fscanf()

描述:可以来自打印机…不只是来自于键盘输入

头文件

 #include 

函数原型

int fscanf(FILE *stream, const char *format, ...);

返回值:

成功:该函数返回成功匹配和赋值的个数

失败:如果到达文件末尾或发生读错误,则返回 EOF

3.2 fprintf()

描述:发送格式化输出到流 stream 中。

头文件

 #include 

函数原型

int fprintf(FILE *stream, const char *format, ...)

返回值:

成功:如果成功,则返回写入的字符总数

失败:返回一个负数

四、fgets() & fputs()

4.1 fgets()

描述:从指定的文件(流 stream )读取一行,并把它存储在 s 所指向的字符串内

头文件

#include 

函数原型

char *fgets(char *s, int size, FILE *stream);
  • s – 可写的空间起始位置
  • size – 长度
  • stream – 从哪个文件取

fgets函数有两种情况可以导致其读取行为正常结束

1.读到了size-1个字节

2.读到了’\n’

返回值

成功:返回空间的起始位置

失败:返回空指针

4.2 fputs()

描述:给我一个字符串的起始位置,把字符串写入到指定的流 stream 中,但不包括空字符。

头文件

#include 

函数原型

int fputs(const char *s, FILE *stream);
  • s – 存放代写入的数组
  • stream – 要被写入字符串的文件

返回值

成功:返回一个非负值

失败:返回EOF(系统宏定义为-1)

4.3 代码演示

#include 
#include 
#include 
#include 

/*
fgets函数有两种情况可以导致其读取行为正常结束:
1读到了size-1个字节 
2读到了'\n'
*/
#define BUFSIZE		5

int main()
{
	FILE *fp;
	char buf[BUFSIZE];

	fp = fopen("tmp.log","r");
	if(fp == NULL)
	{
		perror("fopen()");
		exit(1);
	}
	
	fgets(buf,BUFSIZE,fp);

	puts(buf);

	fgets(buf,BUFSIZE,fp);

	puts(buf);

	fclose(fp);

	exit(0);
}

将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以字符串为单位进行文件拷贝

#include 
#include 
#include 
#include 

#define BUFSIZE 128

int main(int argc,char *argv[])
{
	FILE *fps,*fpd;
	char buf[BUFSIZE];

	if(argc < 3)
	{
		fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
		exit(1);
	}

	fps = fopen(argv[1],"r");
	if(fps == NULL)
	{
		perror("fopen()");
		exit(1);
	}

	fpd = fopen(argv[2],"w");
	if(fpd == NULL)
	{
		fclose(fps);
		perror("fopen()");
		exit(1);
	}

	while(fgets(buf,BUFSIZE,fps) != NULL)
		fputs(buf,fpd);

	fclose(fpd);
	fclose(fps);
	exit(0);
}

五、fread() & fwrite()

头文件

#include 

5.1 fread()

描述:从指定的文件流stream中取数据,取nmemb个对象,一个对象size大小,读到指针所指向的ptr数组中去

函数原型

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr–要存入的数组指针
  • size–读取的每个元素的大小
  • nmemb–对象个数,每个对象的大小为 size 字节
  • stream–指定要读的文件

5.2 fwrite()

描述:从指针ptr所指向的数组中取数据,一个对象size个大小,取nmemb个对象,写到文件指针(流stream)所指向的文件中去

函数原型

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
  • ptr – 要被写入的元素数组的指针
  • size – 要被写入的每个元素的大小
  • nmemb – 元素的个数,每个元素的大小为 size 字节
  • stream – 指定了一个输出文件(流)

返回值

== 成功读入或写出的对象个数==

注意:

fread(buf,1,32,fp);//推荐

数据量充足 = 32

文件中只有5个字节 = 5

fread(buf,32,1,fp);//不推荐

数据量充足 = 1

文件中只有5个字节 = 0

5.3 代码演示

将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以单字节为单位进行文件拷贝

#include 
#include 
#include 
#include 


#define BUFSIZE 1024

int main(int argc,char *argv[])
{
	FILE *fps,*fpd;
	char buf[BUFSIZE];
	int n;

	if(argc < 3)
	{
		fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
		exit(1);
	}

	fps = fopen(argv[1],"r");
	if(fps == NULL)
	{
		perror("fopen()");
		exit(1);
	}

	fpd = fopen(argv[2],"w");
	if(fpd == NULL)
	{
		fclose(fps);
		perror("fopen()");
		exit(1);
	}

	while((n = fread(buf,1,BUFSIZE,fps)) > 0)
		fwrite(buf,1,n,fpd);

	fclose(fpd);
	fclose(fps);

	exit(0);
}

六、fseek() & ftell()

头文件

#include 

6.1 fseek()

描述:设置文件指针位置偏移offset个字节

函数原型

int fseek(FILE *stream, long offset, int whence);
  • stream – 这是指向 FILE 对象的指针(文件流)
  • offset – 这是相对 whence 的偏移量,以字节为单位( -向前偏移,+向后偏移)
  • whence – 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量 描述
SEEK_SET 文件的开头
SEEK_CUR 文件指针的当前位置
SEEK_END 文件的末尾

返回值

成功:0

失败:-1,并且设置errno

6.1.1 rewind()

描述:设置文件位置指针到文件开始处

6.2 ftell()

描述:反馈文件位置指针在哪,以字节为单位开始数

函数原型

long ftell(FILE *stream);
  • stream – 这是指向 FILE 对象的指针(文件流)

返回值

成功:该函数返回位置标识符的当前值(整型数)

失败:则返回 -1L,全局变量 errno 被设置为一个正值。

6.3 代码演示

功能:文件位置指针

#include 
#include 
#include 
#include 

int main()
{
	FILE *fp;

	fp = fopen("tmp.log","r+");
	if(fp == NULL)
	{
		printf("fopen():%s\n",strerror(errno));
		exit(1);
	}		
	fseek(fp,4,SEEK_SET);
	fputc('X',fp);
	fclose(fp);
	exit(0);
}

七、fflush()

描述:刷新流 stream 的输出缓冲区

头文件

#include 

函数原型

int fflush(FILE *stream);
  • stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个缓冲流。

返回值

成功:0

失败:EOF

7.1 拓展

缓冲区:暂存空间,大多数情况下,缓冲区的存在是件好事,作用合并系统调用
行缓冲:stdout,换行时,满了时,强制刷新
全缓冲:默认,(只要不是终端设备,全采用全缓冲模式)满了时,强制刷新
无缓冲:stderr,需要立即输出

7.2 代码演示

缓冲区的作用及分类
功能:五秒之后一口气输出 xxxxx

#include 
#include 
#include 
#include 
#include 

/*
缓冲区:暂存空间,大多数情况下,缓冲区的存在是件好事,作用合并系统调用 
行缓冲:stdout,换行时,满了时,强制刷新
全缓冲:默认,(只要不是终端设备,全采用全缓冲模式)满了时,强制刷新 
无缓冲:stderr,需要立即输出
*/

int main()
{
	int i;
	for(i = 0 ; i < 5; i++)
	{
		putchar('x');
		sleep(1);
	}
	putchar('\n');
/*
	int i= 1;
	printf("Befor while()");
	fflush(stdout);
	while(1);
	printf("After while()");

*/	
	exit(0);
}



八、getline()

描述:用于读取一行 字符直到换行符,包括换行符(如果此行内容超出给定长度,自动重新申请内存)

手册man getline

头文件

#include 

函数原型

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
  • lineptr – 一块可写的数组起始位置

  • n – 用来回调新申请后的空间大小

  • stream – 文件流

返回值

成功:读到的字节个数

失败:-1,设置errno

代码演示
功能:以行为单位,读取给定文件,并输出文件中每行的字符个数

#include 
#include 
#include 
#include 

ssize_t mygetline(char **lineptr, size_t *n, FILE *stream)
{
	char *buf = *lineptr;
	char *cur;
	if(buf == NULL && *n == 0)
	{
		*n = 120;
		buf = malloc(*n);
	}

	while(1)
	{
		*lineptr = fgets(buf,*n,stream);
		if(*lineptr == NULL)
			return EOF;
		if(*n-1 == strlen(buf))
		{
			*n = *n+120;
			buf = realloc(buf,*n);
			fseek(stream,-strlen(buf),SEEK_CUR);
		}
		else
			break;
	}
}

int main(int argc,char *argv[])
{
	FILE *fp;
	char *linebuf = NULL;
	size_t linesize = 0;

	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}

	fp = fopen(argv[1],"r");	
	if(fp == NULL)
	{
		perror("fopen()");
		exit(1);
	}

	while(1)
	{
		if(getline(&linebuf, &linesize, fp) < 0)
			break;
		printf("linesize = %ld\n",linesize);
		printf("strlen(linebuf) = %ld\n",strlen(linebuf));
		printf("linebuf = %s\n",linebuf);
	}

	fclose(fp);
	
	exit(0);
}

文件IO

sysio=>文件io,系统调用io

文件描述符(fd)使用策略:优先使用当前可用范围内最小的fd

Linux中fd为什么返回值为3(从3开始存)呢??

答:在系统创建进程中,建进程时的三个默认的文件描述符 Linux进程默认情况下会有3个缺省打开的文件描述符…所以会返回3.

一、open() && close()

1.1 open()

头文件

#include 
#include 
#include 

函数原型

  • 当文件存在时

    int open(const char* pathname,int flags);
    //文件中有O_CREAT时
    int open(const char *pathname, int flags, mode_t mode);
    
  • 当文件不存在时

    int open (const char* pathname,int flags,int perms)
    

参数

          ┌─────────────┬───────────────────────────────┐
          │fopen() mode │ open() flags                  │
          ├─────────────┼───────────────────────────────┤
          │     r       │ O_RDONLY                      │
          ├─────────────┼───────────────────────────────┤
          │     w       │ O_RDONLY | O_CREAT | O_TRUNC  │
          ├─────────────┼───────────────────────────────┤
          │     a       │ O_WRONLY | O_CREAT | O_APPEND │
          ├─────────────┼───────────────────────────────┤
          │     r+      │ O_RDWR                        │
          ├─────────────┼───────────────────────────────┤
          │     w+      │ O_RDWR | O_CREAT | O_TRUNC    │
          ├─────────────┼───────────────────────────────┤
          │     a+      │ O_RDWR | O_CREAT | O_APPEND   │
          └─────────────┴───────────────────────────────┘
flags 含义
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 可读可写
O_CREAT 无则创建
O_TRUNC 有则清空
O_APPEND 追加末尾
O_NONBLOCK 非阻塞方式打开(默认阻塞方式)
O_NOFOLLOW 打开符号链接本身(默认打开真实文件)

返回值

成功:文件描述符

失败:-1

1.2 close()

头文件

#include 

函数原型

int close(int fd)

返回值

成功:0

失败:-1

二、read() && write()

2.1 read()

描述:从fd所指向的文件中读count个字节到buff缓存区

头文件

#include 

函数原型

ssize_t read(int fd,void *buff,size_t count)

返回值

成功:

  • count:成功读取字节个数
  • 0~count:
    • 剩余文件长度小于count
    • 读取期间被异步信号打断

失败:

  • -1,读取错误

2.2 write()

描述:从buff缓存区读取count个字节写入到fd文件操作符所指向的文件中

头文件

同read函数

函数原型

ssize_t write(int fd,void *buff,size_t count)

返回值

成功:

  • count:成功写入字节个数
  • 0~count:
    • 写入期间被异步信号打断

失败:

  • -1,读取错误

三、复制普通文件小实验

1、打开要复制的文件

fd1 = open(argv[1],O_RDONLY);

2、创建新的文件

fd2 = open(argv[2],O_WRONLY|O_CREAT,0666);

3、把源文件内容读到缓冲区,把缓冲区内容写入新文件

4、循坏执行第三步,直到读取的字节数量为0,退出循坏。

	while(1)
	{
		read_size = read(fd1,buf,512);
		if(read_size == 0) 
			break;
		write(fd2,buf,read_size);
	}

5、关闭打开的文件

	close(fd1);
	close(fd2);

代码内容

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define BUFSIZE 1024

int main(int argc,char *argv[])
{
	int sfd,dfd;
	char buf[BUFSIZE];
	ssize_t len,ret,pos;

	if(argc < 3)
	{
		fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
		exit(1);
	}

	sfd = open(argv[1],O_RDONLY);
	if(sfd < 0)
	{
		perror("open()");
		exit(1);
	}

	dfd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(dfd < 0)
	{
		perror("fopen()");
		exit(1);
	}

	while(1)
	{
		len = read(sfd,buf,BUFSIZE);
		if(len == 0)
			break;
		if(len < 0)
		{
			perror("read()");
			exit(1);
		}

		//len > 0
		pos = 0;
		while(len > 0)
		{
			ret = write(dfd,buf+pos,len);	
			if(ret < 0)
			{
				perror("write()");
				exit(1);
			}
			pos += ret;
			len -= ret;
		}

	}

	close(dfd);
	close(sfd);


	exit(0);
}



}

四、lseek()

描述:设置文件读写位置

头文件

#include 
#include 

函数原型

off_t lseek(int fd, off_t offset, int whence);
  • fd – 文件操作符
  • offset – 这是相对 whence 的偏移量,以字节为单位
  • whence – 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量 描述
SEEK_SET 文件的开头
SEEK_CUR 文件指针的当前位置
SEEK_END 文件的末尾

返回值

成功:该函数返回位置标识符的当前值(整型数)

失败:-1,并且设置errno

代码演示

功能:加深缓冲区的理解,强调在同一个程序中对同一个文件尽量不要混用sysio和stdio

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc,char *argv[])
{

	putchar('a');
	write(1,"b",1);

	putchar('a');
	write(1,"b",1);

	putchar('a');
	write(1,"b",1);


	exit(0);
}



五、dup()

描述:用于将一个文件描述符 oldfd 复制到另一个 newfd

头文件

#include 

函数原型

int dup(int oldfd);
int dup2(int oldfd, int newfd);
  • oldfd – 要被复制的文件描述符

  • newfd – 复制的文件描述符(先关闭newfd文件描述符本身,再将oldfd复制给newfd)

原子操作:一条语句执行时一气呵成,中间不会被分割打断干扰

返回值

如果oldfd与newfd两个文件描述符数值相同,返回newfd。

示例代码
功能:文件描述符的复制(原子操作)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main()
{
	int fd;

	fd = open("/tmp/out",O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(fd < 0)
	{
		perror("open()");
		exit(1);
	}

//	close(1);
//	dup(fd);

	dup2(fd,1);

	if(fd != 1)
		close(fd);

//---------------------------------------------
	puts("Hello!");

	exit(0);
}

拓展提升

UNIX环境高级编程 APUE

UNIX网络编程(2卷) UNP

“圣经(易学难用)”

网络:网络基础,UNP,TCP/IP详解(3)

阅读UNIX环境高级编程章节 5.4,5.8,5.13 , 3.11

面试:三种不同方法获取文件不同长度

  1. while fgetc()
  2. fseek(END),ftell()
  3. stat

标准IO与系统调用IO区别

标准IO:移植性好

系统调用IO:没有缓存区

面试:如何使程序运行速度变快

1.响应速度变快 :多用系统调用IO,吞吐量低

2.吞吐量大:标准IO,提高程序吞吐量,响应速度较慢(一半以上几率追求吞吐量

你可能感兴趣的:(嵌入式软件开发,linux)