标准IO与文件IO

一、标准IO

标准IO:标准I/O是ANSI C建立的一个标准I/O模型,是一个标 准函数包和stdio.h头中的定义,具有一定的可移植性。标准IO库处理很多细节。例如缓存分配,以优化长度执行IO等。标准的IO提供了三种类型的缓存。
(1)全缓存:有4096byte,截止条件有三,一是缓存满的时候,二是调用fflush的时候,三是进程正常结束的时候
(2)行缓存:有1024byte,它与全缓存截止的条件最大的差异就是另外遇到’\n’的时候。shell命令读取就是。
(3)不带缓存:只要用户调用该函数,就会写到内核中,stderr就是。
问:流的刷新时机?
分全缓冲、行缓冲、无缓冲三种情况讨论。

标准IO的几个经常使用函数:

1、fopen:打开一个文件
    #include <stdio.h>
    FILE *fopen(const char *path, const char *mode);

    path:打开文件的路径
    mode:打开文件的权限
        r:只读,文件定位在开头
        r+:读写,文件定位在开头
        w:只写,并且会将内部内容清空,文件定位在开头,文件不存在会被创建
        w+:读写,文件不存在会被创建,文件定位在开头
        a:只写,文件定位在末尾,如果不存在会被创建
        a+:读写,文件定位在末尾,如果不存在会被创建

    返回值:
        成功返回一个文件指针
        失败返回NULL

fopen新建文件权限
  • fopen() 创建的文件访问权限是0666(rw-rw-rw-)
  • Linux系统中umask设定会影响文件的访问权限, 其规则为(0666 & ~umask)
  • 用户可以通过umask函数修改相关设定
#include <stdio.h>
int main(int argc, char *argv[])
{
	FILE *fp;
	if ((fp = fopen(“test.txt”, “r+)) == NULL) 
	{
		printf(“fopen error\n”);
		return -1;
	}
	return 0;
}

2、fclose:关闭文件指针
    #include <stdio.h>
    int fclose(FILE *stream);

    stream:文件操作指针
    
    返回值:
    成功返回0
    失败返回EOF
    	fclose()调用成功返回0, 失败返回EOF, 并设置errno
	
	流关闭时自动刷新缓冲中的数据并释放缓冲区
	
	当一个程序正常终止时, 所有打开的流都会被关闭
	
	流一旦关闭后就不能执行任何操作

3、处理错误信息errno,perror(),strerror()
  • (1) errno就是error number,意思就是错误号码。linux系统中对各种常见错误做了个编号,当函数执行错误时,函数会返回一个特定的errno编号来告诉我们这个函数到底哪里错了。
  • (2) errno是由OS来维护的一个全局变量,任何OS内部函数都可以通过设置errno来告诉上层调用者究竟刚才发生了一个什么错误。
  • (3) linux系统提供了一个函数perror(意思print error),perror函数内部会读取errno并且将这个不好认的数字直接给转成对应的错误信息字符串,然后print打印出来。

extern int errno;				errno 存放错误号

void perror(const char *s);		perror先输出字符串s, 再输出错误号对应的错误信息

char *strerror(int errno);		strerror根据错误号返回对应的错误信息

#include <stdio.h>
int main(int argc, char *argv[])
{
	FILE *fp;
	if ((fp = fopen(“test.txt”, “r+)) == NULL) {
		perror(“fopen”);
		return -1;
} 
/* 上面的程序与下面的程序等价 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
	FILE *fp;
	if ((fp = fopen(“test.txt”, “r+)) == NULL) {
		printf(“fopen: %s\n”, strerror(errno));
		return -1;
} 
4、流的读写操作
4.1、单字符操作c

int fgetc(FILE *stream)

函数功能:从指定的流中读取一个字符。 成功返回读取的字符,读到文件结尾或者失败则返回EOF(-1)

int fputc(int c,FILE* stream)

函数功能: 向指定的流中写入一个字符。成功返回写入的字符,失败返回EOF.

4.2、字符串操作s

char * fgets(char *s,int size,FILE *stream)

函数功能:从流中最多读取size-1个字符到s保存的地址,因为必须预留位置给终止符’\0’,成功返回读入字符串的地址。失败或读到文件的尾部则返回NULL

int fputs( const char *s,FILE*stream)

函数功能: 将一个字符串写入文件,s为字符串的首地址,stream为将要写进去的文件。成功则返回写入字符的个数,失败返回-1

4.3、多字节批量操作

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

功能:从一个指定的流中读取nmemb个对象,每一个对象的大小是size个字节。成功返回读取实际对象的个数(nmemb) 。失败返回0。

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

功能:向一个指定的流中写nmemb个对象。函数正常返回写入实际对象的个数(nmemb) ,失败返回0。

4.4、打印和扫描操作

很折磨人的一点:fprintf与fscanf函数的FILE* 形参在首位!!!而上面其他函数的FILE*形参都在末尾!!!!!!

int fprintf(FILE *stream, const char *format, ...)
功能:打印格式化内容到文件指针当中。
stream:你想要打印的文件指针
format:数据格式,之后这段内容会打印到stream当中
…:%d,%s参数列表

int fscanf(FILE *stream, const char *format, ...)
功能:从指定的文件指针中获取格式化数据。
stream:文件指针
format:指定格式
…:%d,%s参数列表

5、流的刷新(将数据从缓冲区打入文件)

int fflush(FILE *stream)

6、定位流(offset)
6.1、ftell:返回offset值,即当前位置

long ftell(FILE *stream);

6.2、rewind:将操作位置直接定位到文件开头

void rewind(FILE *stream);

6.3、fseek:移动操作位置
    int fseek(FILE *stream, long offset, int whence);
    stream:文件指针
    offset:偏移多少个字节,偏移量
    whence:从哪里开始偏移,基地址
    SEEK_SET(文件头), SEEK_CUR(当前位置), or SEEK_END(文件尾)

    返回值:
        成功返回0
        失败返回-1

7、判断流

int ferror(FILE *stream);
判断文件操作是否出错, 如果出错返回真(非0值)
void clearerr(FILE *stream);
清除文件操作中的错误标志,以不至于每次调用ferror都判断出错

int feof(FILE *stream);
判断文件操作是否到达文件末尾,如果到达则返回真

8、如何实现文件拷贝?
#include <stdio.h>

int main(int argc, char *argv[]){
	int n;
	char buf[30];
	if (argc < 3){
		printf("Usage:%s <src_file> <file_file>\n",argv[0]);
		return -1;
	}
	FILE *fp1, *fp2;
	if ((fp1 = fopen(argv[1], "rb")) == NULL){
		perror("fopen src_file");
		return -1;
	}
	if ((fp2 = fopen(argv[2], "wb")) == NULL){
		perror("fopen dest_file");
		return -1;
	}
	/* fread返回读取的数据数 */
	while((n = fread(buf, sizeof(char), 30, fp1)) > 0){
		fwrite(buf, sizeof(char), n, fp2);
	}
	fclose(fp1);
	fclose(fp2);
	/* 打印调试信息 */
	printf("%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__);
	
	return 0;
}

9、如何实现获取文件大小??
#include <stdio.h>

int main(int argc, char *argv[]){
	FILE *fp;
	
	if (argc < 2){
		printf("Usage: %s %s\n",argv[0],argv[1]);
	}
	if((fp = fopen(argv[1], "r")) == NULL){
		perror("fopen");
		return -1;
	}
	fseek(fp, 0, SEEK_END);
	printf("%ld\n",ftell(fp));
	fclose(fp);

	return 0;
}

二、文件IO

文件IO:文件IO称之为不带缓存的IO(unbuffered I/O)。它们都属于系统调用,只不过在用户层没有缓存,所以叫做无缓存IO,但对于内核来说,还是进行了缓存,只是用户层看不到罢了。

1、open
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);

    pathname:打开的文件名

    flags(操作标志位):
    O_RDONLY, O_WRONLY, or O_RDWR
    以上三个必须从中选择一个写入flags参数中
    以下参数需要掌握一下:

    O_APPEND:追加,在文件后面开始输入
    O_CREAT:创建文件,当文件不存在的情况下创建这个文件,需要在后面添加创建文件的权限(mode)
    创建文件之后的权限是mode&~umask

    O_EXCL:跟上面的创建配套使用,如果文件存在了则打开失败
    O_DIRECTORY:判断文件是否是目录,是目录则打开失败
    O_NONBLOCK or O_NDELAY:不阻塞的打开文件,读写操作的时候不会卡住
    O_TRUNC:打开文件的时候清空里面的内容

    返回值:
    返回一个文件描述符(就是文件的代号)

    注意:
    默认情况下,应用已经打开了三个文件0(标准输入),1(标准输出),2(标准出错)

2、close
    #include <unistd.h>
    int close(int fd);
    fd:关闭哪个文件描述符

    返回值:
    成功返回0,失败返回-1

3、read
    #include <unistd.h>
    ssize_t read(int fd, void *buf, size_t count);

    fd:读取哪个文件
    buf:读取的内容存放到哪里去
    count:读取多少字节的数据

    返回值:
    成功返回读取到多少个字节
    失败返回-1

4、write
    #include <unistd.h>
    ssize_t write(int fd, const void *buf, size_t count);

    fd:写入哪个文件
    buf:写入的内容是什么
    count:写入多少个字节

    返回值:
    成功返回写入多少个字节
    失败返回-1

5、lseek
    #include <sys/types.h>
    #include <unistd.h>
    off_t lseek(int fd, off_t offset, int whence);

    fd:重置哪个文件
    offset:偏移多少个单位(字节)
    whence:从哪里开始偏移
    SEEK_SET:文件开头
    SEEK_CUR:文件当前操作位置
    SEEK_END:文件末尾

    返回值:
    成功返回定位的位置距离头部有多少个字节
    失败返回-1

6、访问目录文件

opendir:打开一个目录文件(不是进入目录)

        #include <sys/types.h>
        #include <dirent.h>

        DIR *opendir(const char *name);
        DIR *fdopendir(int fd);

        name:打开的目录具体位置

        返回值:返回一个目录操作指针
        失败返回NULL

closedir:关闭一个目录文件

        #include <sys/types.h>
        #include <dirent.h>
        int closedir(DIR *dirp);

        dirp:目录指针

        返回值:成功返回0,失败-1

readdir:获得目录中的一个成员结构体

    一次调用只会读目录中一个文件的信息,如果你想要获取一个目录中的所有文件,
那你就要一直读
    #include <dirent.h>
    struct dirent *readdir(DIR *dirp);

    dirp:目录指针

    返回值:
        成功则获取到一个目录中一个成员的结构体,
        失败或者是没有文件可以读了,则返回NULL

    struct dirent {
		ino_t          d_ino;       /* inode number */   //节点数(索引号)
        off_t          d_off;       /* offset to the next dirent *///文件偏移量
        unsigned short d_reclen;    /* length of this record */  //记录长度,比你的文件名要大
        unsigned char  d_type;      /* type of file; not supported by all file system types */ //文件类型
        char           d_name[256]; /* filename */   //文件名
                   };


    判断d_type是否为以下值便可以判断对应的文件类型

         DT_BLK      This is a block device.

         DT_CHR      This is a character device.

         DT_DIR      This is a directory.

         DT_FIFO     This is a named pipe (FIFO).

         DT_LNK      This is a symbolic link.

         DT_REG      This is a regular file.

         DT_SOCK     This is a UNIX domain socket.

         DT_UNKNOWN  The file type is unknown.

7、修改文件权限chomd
       #include <sys/stat.h>

       int chmod(const char *pathname, mode_t mode);
       int fchmod(int fd, mode_t mode);
	
	成功返回0,失败返回EOF与设置errno
8、获取文件属性stat

stat无法识别链接文件,lstat则可以。

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

    int stat(const char *path, struct stat *buf);
    int fstat(int fd, struct stat *buf);
    int lstat(const char *path, struct stat *buf);

    path:你要查看的文件的位置
    buf:存放文件属性的结构体,结构体解释如下:
    返回值:成功返回0,错误返回-1

    struct stat {
               dev_t     st_dev;     /* ID of device containing file */常规文件的ID
               ino_t     st_ino;     /* inode number */ 文件节点数字
               mode_t    st_mode;    /* protection */   文件的类型权限
               nlink_t   st_nlink;   /* number of hard links */ 硬链接数
               uid_t     st_uid;     /* user ID of owner */ 用户ID
               gid_t     st_gid;     /* group ID of owner */    组ID
               dev_t     st_rdev;    /* device ID (if special file) */特殊设备ID
               off_t     st_size;    /* total size, in bytes */ 文件大小,特殊文件判断不了
               blksize_t st_blksize; /* blocksize for file system I/O */块大小,一个页的大小
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */有几块数据,每一块的单位是512字节
               time_t    st_atime;   /* time of last access */      最新访问时间
               time_t    st_mtime;   /* time of last modification */    最新内容更改时间
               time_t    st_ctime;   /* time of last status change */   文件属性更改时间
           };


    如果你是一个常规文件,你想要获取设备节点号major和minor来获取 

    如果你想要获取文件类型:把st_mode传给以下函数就能判断文件类型,如果是则返回真,不是返回假
            S_ISREG(m)  is it a regular file?   //普通文件

            S_ISDIR(m)  directory?      目录文件

            S_ISCHR(m)  character device?   字符设备驱动文件

            S_ISBLK(m)  block device?       块设备驱动文件

            S_ISFIFO(m) FIFO (named pipe)?  管道文件

            S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.) 软链接文件

            S_ISSOCK(m) socket? (Not in POSIX.1-1996.)      套接字文件

9、实现文件拷贝函数

注意点:
1、创建新文件需要给定权限mode

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]){
	int n, fd_src, fd_dest;
	char buf[30];
	if (argc < 3){
		printf("Usage:%s <src_file> <file_file>\n",argv[0]);
		return -1;
	}

	if ((fd_src = open(argv[1], O_RDONLY)) == -1){
		perror("open src_file");
		return -1;
	}
	if ((fd_dest = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1){
		perror("open dest_file");
		return -1;
	}
	while((n = read(fd_src, buf, 30)) > 0){
		write(fd_dest, buf, n);
	}
	close(fd_src);
	close(fd_dest);
	printf("%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__);
	
	return 0;
}

10、实现打印文件目录函数

注意点:
1、目录文件中,每一个item都是一个结构体,必须得逐个读取。
2、需要2个指针,一个目录文件指针,一个是目录项指针。

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>

int main(int argc, char *argv[]){
	if (argc < 2){
		printf("Usag: %s <dir_file>\n", argv[0]);
	}
	DIR *dirp;
	struct dirent *dp;
	if ((dirp = opendir(argv[1])) == NULL){
		perror("opendir");
		return -1;
	}
	while((dp = readdir(dirp)) != NULL){
		printf("%s\n", dp->d_name);
	}
	closedir(dirp);
}
/*
struct dirent {
    ino_t          d_ino;       * inode number *   //节点数(索引号)
    off_t          d_off;       * offset to the next dirent * //文件偏移量
    unsigned short d_reclen;    * length of this record *  //记录长度,比你的文件名要大
    unsigned char  d_type;      * type of file; not supported by all file system types *
	char           d_name[256]; * filename *   //文件名
                   };


    判断d_type是否为以下值便可以判断对应的文件类型

         DT_BLK      This is a block device.

         DT_CHR      This is a character device.

         DT_DIR      This is a directory.

         DT_FIFO     This is a named pipe (FIFO).

         DT_LNK      This is a symbolic link.

         DT_REG      This is a regular file.

         DT_SOCK     This is a UNIX domain socket.

         DT_UNKNOWN  The file type is unknown.
 
 */

11、如何获取文件大小?

size = lseek(fd, 0, SEEK_END);
等价于
fseek(fp, 0, SEEK_END);
size = ftell(fp);

三、两者的区别

  • 文件I/O主要针对文件操作,读写硬盘等,它操作的是文件描述符,标准I/O针对的是控制台,打印输出到屏幕等,它操作的是字符流。对于不同设备得特性不一样,必须有不同api访问才最高效。

  • 无缓存IO操作数据流向路径:数据——内核缓存区——磁盘

  • 标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值