1.Linux标准IO编程

1. 文件系统

在Linux系统中有一个重要的概念:一切皆文件,它把一切资源都看作是文件,包括硬件设备(通常称为设备文件)。

1.1 伪文件系统

除了前面介绍的专门用于存储设备记录文件的文 件系统外,Linux内核还提供了procfs、sysfsdevfs等伪文件系统。
伪文件系统存在于内存中,通常不占用硬盘空间,它以文 件的形式,向用户提供了访问系统内核数据的接口。用户和应用程序 可以通过访问这些数据接口,得到系统的信息,而且内核允许用户修改内核的某些参数。

  • procfs文件系统

procfs是”process filesystem”的缩写,所以它 也被称为进程文件系统,procfs通常会自动挂载在根 目录下的/proc文件夹。procfs为用户提供内核状态和进程信息的接口,功能相 当于Windows的任务管理器,我们也可以通过/proc目录查看一些系统信息。
procfs文件系统详解链接

  • sysfs文件系统

sysfs文件系统通常会自动挂载在根目录下的sys文件夹。sys目录下的文 件/文件夹向用户提供了一些关于设备、内核模块、文件系统以及其他内核组件的信息,如子目录block中存放了所 有的块设备,而bus中存放了系统中所有的总线类型,有i2c,usb,sdi o,pci等。

sysfs文件系统是内核加载驱动时,根据系统上的设备和总 线构成导出的分级目录,它是系统上设备的直观反应,每个设备在sysfs下都有 唯一的对应目录,用户可以通过具体设备目录下的文件访问设备。

  • devfs文件系统

在Linux 2.6内核之前一直使用的是devfs文件系统管理设备,它通 常挂载于/dev目录下。devfs中的每个文件都对应一个设备,用户也可以通过/dev目录下的文件访 问硬件。在sysfs出现之前,devfs是在制作根文件系统的时候就已经固定的,这不太方便使用,而当 代的devfs通常会在系统运行时 使用名为udev的工具根据sysfs目录生成devfs目录。在后面学习制作根文件系统时,就会接触到 静态devfs以及使用udev动态生成devfs的选项。

1.2 虚拟文件系统

除了前面提到的存储器文件系统FAT32、ext4,伪文件系统/proc、/sys、/dev外,还有内 存文件系统ramfs,网络文件系统nfs等等,不同的文件系统标准,需要使用不同的程序逻辑实 现访问,对外提供的访问接口可能也稍有差异。但是我们在编写应用程序时,大都可以通 过类似fopen、fread、fwrite等C标准库函数 访问文件,这都是虚拟文件系统的功劳

在Linux下,一个与文件操作相关的应用程序结构如下图所示:
在这里插入图片描述
总的来说,为了使不同的文件系统共存, Linux内核在用户层与具体文件 系统之前增加了虚拟文件系统中间层,它对复杂的系统进行抽象化,对用户提供了统 一的文件操作接口。无论是ext2/3/4、FAT32、NTFS存储的文件,还是/proc、/sys提供 的信息还是硬件设备,无论内容是在本地还是网络上,都使用 一样的open、read、write来访问,使得”一切皆文件”的理念被实现,这也正是软件中间层的魅力。

2. 标准IO

2.1 什么是标准IO?

标准IO指的是ANSI C中定义的用于IO操作的一系列函数。

例如Windows系统打开文件操作的系统API为OpenFile,Linux则为open,C标准库都把它们封装为fopen,Windows下的C库会通过fopen调用OpenFile函数实现操作,而Linux下则通过glibc调用open打开文件。而用户代码如果使用标准IO函数例如fopen,那么只要根据不同的系统重新编译程序即可,而不需要修改对应的代码。

2.2 流的含义是什么?

​标准IO的核心对象就是流。当我们使用标准IO打开一个文件时,就会创建一个FILE结构体和实际打开的文件关联起来,我们把这个FILE结构体形象地称为流。标准IO函数都是基于流进行各种操作。[用户程序运行时系统自动打开的三个预定义流:](stdin – 标准输入流, stdout – 标准输出流 ,stderr – 标准错误输出流)

标准IO中流的缓冲类型:

  1. 全缓冲:在这种情况下,当填满缓标准IO的缓冲区后才能进行实际的IO操作。对于存放在磁盘中的普通文件,用标准IO打开时默认是全缓冲的:当缓冲区已满或者执行flush这种情况缓冲的操作时才会进行磁盘操作。
    全缓存清空条件:
    1.函数结束
    2.缓存区满
    3.强制刷新 fflush()
    4.缓存区要被其他函数使用
  2. 行缓冲:在这种情况下,当在输入和输出中遇到换行符时执行IO操作,标准输入流和标准输出流就是使用行缓存的例子。
    行缓存清空条件:
    1. 函数结束
    2. 缓存区满
    3. 数据中有 ‘\n’
    4. 强制刷新 fflush()
    5. 缓存区要被其他函数使用 scanf();
  3. 无缓冲:不对IO操作进行缓冲,即对流的读写时会立刻操作实际文件。例如标准错误流就是不带缓冲的,这样使得出错信息可以立刻显示在终端上,而不管输出的内容是否包含换行符。

2.3* 流的操作#include <stdio.h>

2.3.1 流的打开

打开流的函数有fopen()fdopen()freopen(),这些函数打开成功均返回指向FILE的指针打开失败均返回NULL.
函数原型:
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);//可指定打开文件的路径和模式,且还可指定特定的IO流
mode:
r: 以只读方式打开文件,文件必须要是已经创建好的.
r+:以读写方式打开文件,文件必须要是已经创建好的.
w: 以只写方式打开文件,截断方式打开文件(清空),如果文件不存在则自动创建文件.
w+: 以读写方式打开文件,截断方式打开文件(清空),如果文件不存在则自动创建文件.
a: 以只写方式打开文件,追加方式打开文件,如果文件不存在则自动创建文件.
a+: 以读写方式打开文件,追加方式打开文件,如果文件不存在则自动创建文件.

2.3.2 流的关闭

关闭流的函数有fclose(),该函数将流的缓冲区的数据全部写入文件中,并释放相关资源和关闭文件。
return value:成功返回0,失败返回EOF
函数原型:int fclose(FILE *stream);
The fclose() function flushes the stream pointed to by stream (writing any buffered output data using
fflush(3)) and closes the underlying file descriptor.

流的关闭中包含fflush()函数,fflush()函数用于把尚未写到文件的内容立即写入或把未读出的立即读出。常用于确保前面操作的数据被写入到磁盘上。fflush的函数原型如下:
int fflush(FILE *stream);
description:
For output streams, fflush() forces a write of all user-space buffered data for the given output or up‐date stream via the stream's underlying write function. For input streams associated with seekable files (e.g., disk files, but not pipes or terminals), fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.The open status of the stream is unaffected.If the stream argument is NULL, fflush() flushes all open output streams. For a nonlocking counterpart, see unlocked_stdio(3).

2.3.3 流的读写

2.3.3.1 按字符(字节)输入/输出

字符输入/输出函数每次仅读写一个字符,常用的函数有getc()\fgetc()\getchar()\putc()\fputc()\putchar().
函数原型:
int fgetc(FILE *stream);
fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.

int getc(FILE *stream);
getc() is equivalent to fgetc() except that it may be implemented as a macro which evaluates stream more than once.

int getchar(void);
getchar() is equivalent to getc(stdin).
return: fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.


int fputc(int c, FILE *stream);
fputc() writes the character c, cast to an unsigned char, to stream.

int putc(int c, FILE *stream);
putc() is equivalent to fputc() except that it may be implemented as a macro which evaluates stream more than once.

int putchar(int c);
putchar() is equivalent to putc(c, stdout).
return: fputc(), putc() and putchar() return the character written as an unsigned char cast to an int or EOF on error.

2.3.3.2 按行输入/输出

行输入/输出函数一次操作一行。常用函数有:gets()\fgets()\puts()\fputs()
函数原型:
char *fgets(char *s, int size, FILE *stream);
fgets()从指定的流中读取一份字符串,当遇到\n时,会读取\n或读取size-1个字符后返回,注意:fgets()
不能保证每次都能读出一行。
fgets() reads in at most one less than size characters from stream and stores them into the buffer
pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the
buffer. A terminating null byte (’\0’) is stored after the last character in the buffer.
return: fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.

fgets的用法.链接

char *gets(char *s);
Never use this function.
gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with a null byte (’\0’). No check for buffer overrun is performed (see BUGS).


int fputs(const char *s, FILE *stream);
fputs() writes the string s to stream, without its terminating null byte (’\0’).

int puts(const char *s);
puts() writes the string s and a trailing newline to stdout.

return: puts() and fputs() return a nonnegative number on success, or EOF on error.


2.3.3.3* 已指定大小为单位读写文件

在文件流被打开之后,可以对文件流按指定大小为单位进行读写操作。常用函数有fread()/fwrite()
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
The function fread() reads nmemb items of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
The function fwrite() writes nmemb items of data, each size bytes long, to the stream pointed to by
stream, obtaining them from the location given by ptr.
return: On success, fread() and fwrite() return the number of items read or written. This number equals the num‐ber of bytes transferred only when size is 1. If an error occurs, or the end of the file is reached, the return value is a short item count (or zero).
fread() does not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to
determine which occurred.

  • ptr: 存放读入记录的缓冲区
  • size: 读取每个记录的大小
  • nmemb: 写入的记录数
  • stream: 要写入的文件流
  • return:成功则返回实际写入的nmemb数目,fread()失败(或者阅读到文件尾)则返回???需要试验看现象

2.3.4 流的定位

每个打开的流内部都有一个当前的读写位置。流在打开时,当前读写位置为0,表示文件的开始位置。每读写一次后,当前读写位置自动增加实际读写的大小,文件指针随之移动。在读写流之间可以先对流进行定位,即移动到指定位置再操作。常用的流定位函数有:fseek()/ftell()
函数原型:
int fseek(FILE *stream, long offset, int whence);
The fseek() function sets the file position indicator for the stream pointed to by stream. The new position, measured in bytes, is obtained by adding offset bytes to the position specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to the start of the file, the current position indicator, or end-of-file, respectively. A successful call to the fseek() function clears the end-of-file indicator for the stream and undoes any effects of the ungetc(3) function on the same stream.

  • stream: 要定位的文件流
  • offset: 相对于基准值的偏移量
  • whence: 基准值
    SEEK_SET 代表文件起始位置
    SEEK_END 代表文件结束位置
    SEEK_CUR 代表文件当前读写位置

return: 成功返回0,失败返回EOF


long ftell(FILE *stream);//获取当前文件的读写位置,并且以整数的形式返回.
The ftell() function obtains the current value of the file position indicator for the stream pointed to by stream.
rerturn: 成功则返回当前读写位置,失败则返回EOF.

void rewind(FILE *stream)//把文件读写位置设置到文件开头. 相当于 fseek(fp, 0, SEEK_SET);

2.3.5 错误处理

标准IO函数执行时如果出现错误,会把错误码保存在全局变量errno中,我们可以通过perror()/strerror()打印错误信息。
函数原型:void perror(const char *s);
char * strerror(int errnum);
示例:

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

int main(int argc, char *argv[])
{
	FILE *fp;
	if ( (fp = fopen("1.txt","r")) == NULL)
	{
		perror("fpoen");
		return -1;
	}
	fclose(fp);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
	FILE *fp;
	if ((fp = fopen("1.txt","r+")) == NULL)
	{
		printf("fopen failed: %s\n",strerror(errno));
		return -1;
	}
	fclose (fp);
    return 0;
}

strerror与perror的区别.链接


2.3.6 格式化输入/输出

格式化输入/输出是指可以指定输入/输出的具体格式,常用函数有printf()/scanf()/fscanf()/sscanf()/fprintf()/sprintf()
成功输出字符数,失败返回EOF,具体可通过man手册查看
C语言中printf函数中“%d,%f”等含义:

  • %d:有符号整数int输出,%ld:长整型输出
  • %o: 以无符号八进制数形式输出整数
  • %x: 以无符号十六进制数形式输出整数
  • %u: 以十六进制数输出unsigned型int数据(无符号数)
  • %c: 用来输出一个字符
  • %s: 用来输出一个字符串
  • %f: 用来输出实数,以float浮点型小数形式输出
  • %m.nf: 输出共占m列,其中有n位小数,若数值宽度小于m左端补空格
  • %e: 以指数形式输出实数
  • %g: 根据大小自动选f格式或e格式,且不输出无意义的零
  • %p: 输出指针所指向的地址

输出指定长度:

  • 输出指定长度的字符串,str_len为指定的长度

    printf("%.*s\n",str_len,str);
    
  • 输出指定长度的字符串,超长时不截断,不足时右对齐

    printf("%ns",str);//n为指定长度的10进制数
    
  • 输出指定长度的字符串,超长时不截断,不足时左对齐

    printf("%-ns",str);//n为指定长度的10进制
    
  • 输出指定长度的字符串,超长时截断,不足时右对齐

    printf("%n.ms",str);//n最终的字符串输出长度
    					  //m为从参数字符串中取出的子串长度
    
  • 输出指定长度的字符串,超长时截断,不足时左对齐

    printf("%-n.ms",str);//n最终的字符串输出长度
    					  //m为从参数字符串中取出的子串长度
    

参考链接


本文只做个人学习交流用,其中部分知识点来自华清远见、野火及互联网,如有侵权,立即删除。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值