文件IO
- Linux文件
- 系统IO
- 文件IO
Linux文件
Linux系统下全是文件,一个.c是一个文件,一个文件夹也是一个文件,每当一个文件被创立,系统会为这个文件分配一个空间在硬件中,然后创立了一个结构体inode。这个结构体里面保存了这个文件的所有信息,指向了分配内存的地方。每当这个文件被打开的时候,系统会创立一个file结构体,这个结构体用来描述一个已经打开的文件,里面有文件状态标记(O_RDONLY…),文件偏移量/offset(光标),还有一个指向inode的指针,然后一个打开的文件会创立一个唯一的ID,文件的打开必然伴随着进程的进行,每当一个进程启动后,会在内核中创立一个PCB进程控制块,这个PCB有一个已经打开的文件描述符表,这个表中记录着该进程中所有的打开的文件描述符和file指针,一般每个进程创立都会伴随着三个文件描述符 0 1 2 分别代表标准输入、标准输出和标准错误。之后的就是3 4 …
inode
概述:inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间、档名、使用者及群组等。inode分为内存中的inode和文件系统中的inode,为了避免混淆,我们称前者为VFS inode, 而后者以EXT2为代表,我们称为Ext2 inode。在阅读文件系统源码的时候,必须对这个结构有着清醒的认识。下面分别对VFS inodee与Ext2 inode做一下简单的描述:
VFS inode:
VFS inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。inode结构中的静态信息取自物理设备上的文件系统,由文件系统指定的函数填写,它只存在于内存中,可以通过inode缓存访问。虽然每个文件都有相应的inode结点,但是只有在需要的时候系统才会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,我们可以通过遍历这个链表去得到我们需要的文件结点,VFS也为已分配的inode构造缓存和哈希表,以提 高系统性能。inode结构中的struct inode_operations *i_op为我们提供了一个inode操作列表,通过这个列表提供的函数我们可以对VFS inode结点进行各种操作。每个inode结构都有一个i结点号i_ino,在同一个文件系统中每个i结点号是唯一的。
VFS inode:
struct inode {
struct list_headi_hash;
struct list_headi_list;
struct list_headi_dentry;
struct list_headi_dirty_buffers;
unsigned longi_ino; /*每一个inode都有一个序号,经由super block结构和其序号,我们可以很轻易的找到这个inode。*/
atomic_t i_count; /*在Kernel里,很多的结构都会记录其reference count,以确保如果某个结构正在使用,它不会被不小心释放掉,i_count就是其reference count。*/
kdev_t i_dev; /* inode所在的device代码 */
umode_t i_mode; /* inode的权限 */
nlink_t i_nlink; /* hard link的个数 */
uid_t i_uid; /* inode拥有者的id */
gid_t i_gid; /* inode所属的群组id */
kdev_t i_rdev; /* 如果inode代表的是device的话,那此字段将记录device的代码 */
off_t i_size; /* inode所代表的档案大小 */
time_t i_atime; /* inode最近一次的存取时间 */
time_t i_mtime; /* inode最近一次的修改时间 */
time_t i_ctime; /* inode的产生时间 */
unsigned long i_blksize; /* inode在做IO时的区块大小 */
unsigned long i_blocks; /* inode所使用的block数,一个block为512 byte*/
unsigned long i_version; /* 版本号码 */
unsigned short i_bytes;
struct semaphore i_sem;
struct rw_semaphore i_truncate_sem;
struct semaphore i_zombie;
struct inode_operations *i_op;
struct file_operations *i_fop;/* former ->i_op->default_file_ops */
struct super_block *i_sb; /* inode所属档案系统的super block */
wait_queue_head_t i_wait;
struct file_lock *i_flock; /* 用来做file lock */
struct address_space *i_mapping;
struct address_space i_data;
struct dquot *i_dquot [MAXQUOTAS];
/* These three should probably be a union */
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct char_device *i_cdev;
unsigned longi_dnotify_mask; /* Directory notify events */
struct dnotify_struct *i_dnotify; /* for directory notifications */
unsigned long i_state; /* inode目前的状态,可以是I_DIRTY,I_LOCK和 I_FREEING的OR组合 */
unsigned int i_flags; /* 记录此inode的参数 */
unsigned char i_sock; /* 用来记录此inode是否为socket */
atomic_t i_write count;
unsigned int i_attr_flags; /* 用来记录此inode的属性参数 */
__u32 i_generation;
union {
struct minix_inode_info minix_i;
struct ext2_inode_info ext2_i;
struct ext3_inode_info ext3_i;
struct hpfs_inode_info hpfs_i;
struct ntfs_inode_info ntfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct sysv_inode_info sysv_i;
struct affs_inode_info affs_i;
struct ufs_inode_info ufs_i;
struct efs_inode_info efs_i;
struct romfs_inode_info romfs_i;
struct shmem_inode_info shmem_i;
struct coda_inode_info coda_i;
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
struct qnx4_inode_info qnx4_i;
struct reiserfs_inode_info reiserfs_i;
struct bfs_inode_info bfs_i;
struct udf_inode_info udf_i;
struct ncp_inode_info ncpfs_i;
struct proc_inode_info proc_i;
struct socketsocket_i;
struct usbdev_inode_info usbdev_i;
struct jffs2_inode_infojffs2_i;
void *generic_ip;
} u;
};
系统IO
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);
函数作用:找到路径,打开文件,创建一个新的文件描述符(文件或设备)。
返回值:一个int类型的描述符-1表示打开失败 >2表示打开成功,失败被error设置
pathname:路径 绝对路径相对路径都可以
flag:打开文件的标记 或者说是状态
总共8个
O_RDONLY:read only 只读
O_WRONLY:write only 只写
O_RDWR:read and write 可读可写
以上者三个标记只能选一个(文件打开的方式)
O_APPEND:追加标记,打开文件后,文件偏移量会站在文件末尾
O_CREAT:创建标记,如果不存在,则创建它
O_EXCL:该标志一般来O_CREAT配合使用,用来测试文件是否存在
如果执行 O_CREAT|O_EXCL,如果文件存在,则open会失败
并且errno==EEXIST
O_TRUNC:truncate 截短 文件截短,把文件内容清0
O_NONBLOCK:非阻塞方式打开文件
非阻塞:“不等待”
如果文件没有内容,read不会阻塞,直接返回一个错误
如果文件没有空间啦,write不会阻塞,直接返回一个错误
阻塞:等待
如果文件没有内容,read会阻塞(直到有数据或出错)
如果文件没有空间,write会阻塞(直到可写或出错)
...
多个标记“|”连接 0000 0001 | 0000 0010
如:
O_RDWR | O_CREAT | O_TRUNC
mode:创建文件的权限,一般是flag中有O_CREAT的时候用
0 rwx rwx rwx
r:可读
w:可写
x:可执行
最大是0777,是以八进制存储
1)S_I**R**WXU S_I**R**USR S_I**W**USR S_I**X**USR "用户"
2)S_IRWXG S_IRGRP S_IWGRP S_IXGRP “组用户”
3)S_IRWXO S_IROTH S_IWOTH S_IXOTH “其他用户”
在这里的位设置为1是表示这个位置被允许。
read
#include <unistd.h>
size_t read(int fd, void *buf, size_t count);
函数作用:从与文件描述符 fd 相关联的文件中读取前count 个字节到缓冲区buf 中。
返回值:返回一个int类型的数据,代表着读到buf里面的数据字节,如果是-1则读入失败,0则光标已经到了文件末尾>0则是读进buf的字节。
fd:文件描述符
buf:一个void*型指针,可以是数组的数组名
count:要读入buf的字节
NOTE:
文件偏移量(“光标位置”会由内核自动维护)
一般来说,打开文件时,offset = 0,每次成功
read/write了count 个字节后。offset+=count
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
函数作用:把缓冲区buf 的前count 个字节写入与文件描述符 fd 相关联的文件中。
返回值:返回写入的字节数,-1:写入错误 0:光标到结尾了 >0:成功读入。
fd:文件描述符
buf:一个void*型指针,可以是数组的数组名
count:要读入buf的字节
close
#include <unistd.h>
int close(int fd);
函数作用:终止文件描述符fd 和对应文件(文件或设备)的关联。文件描述符被释放并能够重新使用。close 调用成功返回0,出错返回 -1。
返回值:成功返回0失败返回-1,同时errno被设置**
fd:文件描述符
lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
函数作用:lseek 对文件描述符 fd 的读写指针进行设置。也就是说,设置文件的下一个读写位置。可根据绝对位置和相对位置(当前位置或文件尾部)进行设置。
返回值:返回从文件头到文件指针被设置处的字节偏移值,失败返回-1。
fd:文件标识符。
wence:
1)SEEK_SET: offset 是一个绝对位置。
2)SEEK_CUR: offset 是相对于当前位置的一个相对位置。
3)SEEK_END: offset 是相对于文件尾的一个相对位置。
error
errno是一个全局变量
#include <errno.h>
是用来保存最后一个出错的错误码
perror把错误码对应的提示打印出来
perror("user indi")
char *strerror(int errnum);
把errnum的错误显示出来
*****ioctl系统调用
。。。等待填写
dup&dup2
dup
#include <unistd.h>
int dup(int oldfd);
函数作用:dup用来复制参数oldfd所指的文件描述符。当复制成功是,返回最小的尚未被使用过的文件描述符,若有错误则返回-1.错误代码存入errno中返回的新文件描述符和参数oldfd指向同一个文件,这两个描述符共享同一个数据结构,共享所有的锁定,读写指针和各项全现或标志位。
oldfd:老的文件标识符。
返回值:返回新的文件标识符,一般是按照顺序加,比如old是3,则返回4(如果4没有空缺的话)。
NOTE:调用dup(oldfd)等效于 fcntl(oldfd, F_DUPFD, 0)
STDIN_FILENO:接收键盘的输入
STDOUT_FILENO:向屏幕输出
fileno(stdin)也是一样的
dup2
#include <unistd.h>
int dup2(int oldfd, int newfd);
函数作用:dup2与dup区别是dup2可以用参数newfd指定新文件描述符的数值。若参数newfd已经被程序使用,则系统就会将newfd所指的文件关闭,若newfd等于oldfd,则返回newfd,而不关闭newfd所指的文件。dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写位置和各项权限或flags等。
返回值: 若dup2调用成功则返回新的文件描述符,出错则返回-1.
NOTE:实际上就是把newfd代表的数字指向oldfd,也可以在参数里面直接输入数字,这里的例子是把newfd(1)传入,所以1这个文件标识符代表的其实是oldfd的file。
dup2(oldfd, newfd)等效于
close(oldfd);
fcntl(oldfd, F_DUPFD, newfd);
在shell的重定向功能中,(输入重定向”<”和输出重定向”>”)就是通过调用dup或dup2函数对标准输入和标准输出的操作来实现的。
uamsk
#include <sys/types.h>
#include <sys/stat.h>
umask用来设置一个文件创建的掩码(mask,掩码中为1的bit你在创建文件
不能指定这个权限的 例如 000 010 010 表示组用户和其他用户不能对文件进行写操作)
默认情况下,创建文件时,不能指定S_IWGRP和S_IWOTH //rwx rwx rwx
mode_t umask(mode_t mask);
这个有一个函数还有一个指令
在Linux上运行代码umask可以查看此文件的控制权限
Note:
这个0022展开来是0(特殊权限) 000 010 010 表示在这里创建的文件组成员和其他成员不能对他进行修改(这是在进程中没有用umask函数的默认情况或者没有用umask函数的创建情况,如果用了umask函数那么这个文件的umask就不起作用了。)
获取进程的当前工作目录
getwd
#include <unistd.h>
char *getwd(char *buf);
buf:用来保存进程当前的工作目录的绝对路径,“字符串”。
返回值:成功,返回当前工作目录字符串的首地址失败,返回NULL,同时errno被设置。
getwd 有一个巨大的BUG!!!你懂的。有越界的风险!
如果buf所指向的空间,不足够大(当前的目录字符串,超过Buf所指向的空间的长度),
getwd就有可能会访问buf后面的内存空间,这样有可能会造成内存的非法访问。
getcwd
#include <unistd.h>
char *getcwd(char *buf, size_t size);
buf:指向的空间来保存当前的工作目录路径字符串的
size:指向Buf指向的空间的长度。
返回值:成功,返回当前工作目录字符串的首地址失败,返回NULL,同时errno被设置。
NOTE:如果当前工作目录的路径字符串的长度>size-1,这个函数就会报错
get_current_dir_name
#include <unistd.h>
char *get_current_dir_name(void);
返回值:成功,返回当前工作目录字符串的首地址失败,返回NULL,同时errno被设置。
get_current_dir_name也是用来获取进程当前工作目录的绝对路径
只不过,这个函数在内部会自动malloc足够长的空间来保存当前的
工作目录的路径名,并且返回这个首地址。
所以,为了防止内存泄漏,调用者使用完后,要free这个空间!!!
要用这个函数这个define一定要放在头文件的前面,不然会waring
改变进程当前的工作目录
chdir&fchdir
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
函数作用:现在正在运行一个进程,调用这个函数之后会改变这个整个进程的运行路径。也就是把这个进程换个地方运行,例如umask在共享文件夹内会有bug,可以改变到home文件夹里面。
path:要切换的工作目录。
fd:文件描述符。
返回值:成功返回0,失败:返回-1并且errno被设置。
**********文件锁fcntl()&多路复用
文件截短 truncate ftruncate
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
truncate:
path:要截短的文件路径
length:截短后的文件内容的长度
ftruncate:
fd:要截短的文件描述符所指向的文件
length:截短后的文件内容的长度
length <源文件的长度
“截短”
length >源文件的长度
“扩展 留空洞”
(fruncate也是一样)
函数作用:把一个文件截短成想要的长度
返回值:
成功返回0
失败返回-1 并且errno被设置
删除一个文件 unlink rmdir remove
unlink:删除一个普通文件或它的节点
#include <unistd.h>
int unlink(const char *pathname);
返回值:成功返回0,失败返回-1并且errno被设置
pathname:要删除文件的路径
Note:
unlink把要删除的那个文件的inode删除(仅做了一个标记
标记该inode free的状态)
rmdir
#include <unistd.h>
int rmdir(const char *pathname);
返回值:成功返回0,失败返回-1并且errno被设置
pathname:要删除文件的路径
remove
#include <stdio.h>
int remove(const char *pathname);
函数作用:删除一个文件或者一个空的文件夹
返回值:成功返回0,失败返回-1并且errno被设置
pathname:要删除文件的路径
获取文件属性stat, fstat, lstat, fstatat
stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
函数作用:得到一个文件的属性信息,应该不是全部的inode信息,只是一部分信息。
返回值:成功返回0,失败返回-1并且errno被设置
pathname:想要得到的文件信息的路径名称
fd:和pathname差不多只不过是文件标识符
statbuf:指向struct stat结构体,结构体里面存了pathname的信息
Note:
lstat功能stat相同,只不过,当path是一个符号链接时,lstat获取的是符号
链接文件本身的属性信息。而stat获取的是符号链接文件指向的那个文件的属性信息。
stat/fstat/lstat获取文件的状态信息的,实际就是获取文件一个struct stat的结构体内容
struct stat {
dev_t st_dev; /* 设备号*/
//st_dev用来表示容纳该文件的那个设备的设备号
ino_t st_ino; /* Inode number */
//该文件的inode的编号
mode_t st_mode; /* File type and 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.)
为真则表示该文件是 网络编程
st_mode还包含了文件的权限位,可以用如下代码来解析
if(st.st_mode & S_IRUSR)
{
//user有read的权限
}
else
{
//user没有read的权限
} 在这里插入代码片
....
S_IWUSR S_IXUSR
S_IRGRP S_IWGRP S_IXGRP
S_IROTH S_IWOTH S_IXOTH
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) */
设备号(加入该文件是一个设备)
off_t st_size; /* Total size, in bytes */
文件的内容的大小
对于普通文件,文件内容的大小
对于符号链接(软连接文件),文件内容是什么?
指向的那个文件的文件名
目录文件的内容是什么?
目录项数组
blksize_t st_blksize; /* Block size for filesystem I/O */
块大小(与具体的硬件设备有关)
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
该文件占多少块(512字节为一块)
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
最后访问时间
struct timespec st_mtim; /* Time of last modification */
最后修改时间
struct timespec st_ctim; /* Time of last status change */
最后修改文件属性的时间
#define st_atime st_atim.tv_sec /* Backward compatibility for Linux 2.6 */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
目录操作 opendir,readdir(还有很多函数没写)
opendir & fopendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
获取path子目录下的所由文件和目录的列表,如果path是个文件则返回值为NULL
DIR *fdopendir(int fd);
错误代码:
1、EACCESS 权限不足。
2、EMFILE 已达到进程可同时打开的文件数上限。
3、ENFILE 已达到系统可同时打开的文件数上限。
4、ENOTDIR 参数name 非真正的目录。
5、ENOENT 参数name 指定的目录不存在, 或是参数name 为一空字符串。
6、ENOMEM 核心内存不足。
函数作用:通过文件路径或者文件标识符(fd)来获得文件的的DIR信息,后面的函数可以通过这个返回值来操作文件。
name:文件路径。
fd:文件标识符。
返回值:返回一个DIR指针。
DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构
保存当前正在被读取的目录的有关信息(摘自《UNIX环境高级编程(第二版)》)
struct __dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;
这个结构体由以下几个函数调用:
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
问题:
---------------------------------------------------------------
那么我在想,用open的时候会创建一个file结构体,这个结构体存储了inode节点,
用了open肯定会有个进程,然后在进程里面由一个有一个文件标识符表,表里面
的数字就指向了打开的文件file,那么如果用opendir打开的文件夹有没有这个文件
标识符呢?如果有文件标识符是多少呢?他返回的是一个结构体,而且这个fopendir
可以传入open返回的fd参数,意思就是代表可以通过文件标识符来打开这个目录。
我猜测每个文件都有每个文件打开的方式,普通文件可以通过open打开,目录文件
就要通过opendir打开,那进程里面是怎么通过DIR来找到这个inode的呢?
我看那个结构体里面的成员变量感觉像是从路径获得一个fd(可能也是调用了open
函数),然后实际上传入的DIR中就包含了fd,也就是说现在有了一个file结构体
变量,但是那书上说他是一个和file类似的结构体,所以他绝对不是file,是一个
和file有着差不多功能的结构体。如果没有file的话,也就是说没有调用open函数。
所以得知(我猜的)DIR也就是一个标识符,和fd大同小异。现在先可以不管这个
主要是看之后的函数调用这个返回值。
---------------------------------------------------------------
readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
目录:创建一个空目录时,系统为它预留一个目录项的数组当载这个目录下创建一个文件或目录,就会填充一个目录项到“目录项数组”中去。
目录项:struct dirent结构体,在文件被创建的时候,系统会识别这是目录文件还是普通文件还是其他文件,如果是目录文件,系统会为他分配4096个内存空间,空间内可以存放其他的文件指针,通过这个指针可以遍历所有的文件,而通过这个指针信息可以找到文件的名字也就是 char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */ ,通过stat函数就可以找到这个文件的具体信息了,现在目前最主要就是通过这个方法找到文件的具体信息。
遍历目录:通过下一个指针的返回值找到下一个目录,这样循环遍历,知道文件末尾返回NULL。
dirp:DIR指针。
返回值:返回一个struct dirent的指针,也就是返回下一个目录项的地址,这样可以通过指针的链接来找到下一个目录项,
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
文件IO
流stream
fopen
#include<stdio.h>
FILE *fopen(const char *path,const char *mode)
函数作用:找到文件,打开文件,并将文件内的数据加载到流内。
path:要打开的文件路径。
mode:要打开的格式。
┌─────────────┬───────────────────────────────┐
│fopen() mode │ open() flags │
├─────────────┼───────────────────────────────┤
│ r │ O_RDONLY │
├─────────────┼───────────────────────────────┤
│ w │ O_WRONLY | 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 │
└─────────────┴───────────────────────────────┘
返回值:返回一个FILE类型的指针。失败返回NULL。
typedef struct
{
short level; //缓冲区满/空的状态
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如缓冲区无内容则不读取字符
short bsize; //缓冲区的大小
unsigned char *buffer; //数据缓冲区的位置
unsigned char *curp; //指针当前的指向
unsigned istemp; //临时文件指示器
short token; //用于有效性检查
}FILE;
老师笔记:
文章描述:
意思就是创建了一个FILE之后就会有一个缓存区,这个缓存区分为三类。
fclose
#include <stdio.h>
int fclose(FILE *stream);
stream:要关闭的文件的FILE*
返回值:成功返回0,失败返回EOF(-1)同时errno被设置
文件出错/结束标记EOF & feof & 缓存区
参考文章
参考文章
EOF就是个文本文件流的结束标志符
feof文本和二进制都可以用
缓存区:
过了10s才出来的12345678910
读写流 头文件全是 #include<stdio.h>
getc & fgets & getchar
int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void)
函数说明:fgetc和getc用来从stream指定的文件流中,读取一个字符。并把读到的字符的ASCII码输出,不同的是fgetc是一个函数,getc是宏实现 。getchar等效于getc(stdin)。
返回值:成功返回读到那个字符的ASCII码,失败返回EOF(-1)同时errno被设置。
putc & fputc & putchar
int putc(int c, FILE *stream);
int fputc(int c, FILE *stream);
int putchar(int c)
函数说明:这三个函数用来把c指定的字符,输出到stream指定的文件流中去,putc与fputc功能和返回值一样。
返回值:成功返回实际写入到文件流中的字符的ASCII码 c的ascii码,失败 返回EOF(-1) 并且errno被设置
gets & fgets
char *gets(char *s);
char *fgets(char *s, int size, FILE *stream);
gets函数说明:gets用来从标准输入流stdin获取一行字符,存储到s指定的内存空间中去。
s:指向的空间,用来存储从输入缓冲区获取到的多个字符。
返回值:成功返回s的首地址,失败返回NULL,并且errno被设置
fgets函数说明:
s:从文件流中读取数据保存到s窒息昂的空间中。
size:表示你最多获取size个字节,size一般为s,指向的空间的可用长度。
stream:FILE*指针,表示从哪个文件流中读取数据。
这里读取了20个字符
puts & fputs
int puts(const char *s);
int fputs(const char *s, FILE *stream);
函数说明:fputs用来把s所指向的字符串,输出到stream指向的文件流中去。
s:指向要输出的字符串的首地址
stream:FILE* 表示我们要输出到哪个文件流中去
返回值:成功返回一个非负数,失败返回EOF(-1),同时errno被设置。
puts(s) <==> fputs(s,stdout);
fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
_IO_size_t _IO_fread (void *buf,_IO_size_t size,_IO_size_t count,_IO_FILE *fp)
{
_IO_size_t bytes_requested = size * count;
_IO_size_t bytes_read;
CHECK_FILE (fp, 0);
if (bytes_requested == 0)
return 0;
_IO_acquire_lock (fp);
bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
_IO_release_lock (fp);
return bytes_requested == bytes_read ? count : bytes_read / size;
}
函数说明:fread用来从stream指定的文件流中,读取N个对象每个对象size字节 读多少 nmemb个 共nmemb*size个字节放到ptr所指向的内存空间中去。
ptr:指向的内存空间,用来保存从文件流中获取的数据。
size:每块所占的字节数。
nmemb:有多少块。
stream:表示要从那个文件流中读取数据。
返回值:成功返回实际读取到的块的个数!<=nmemb,失败返回-1,同时errno被设置。
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
_IO_size_t _IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
_IO_size_t request = size * count;
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
return 0;
_IO_acquire_lock (fp);
if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
written = _IO_sputn (fp, (const char *0 buf, request);
_IO_release_lock (fp);
if (written == request)
return count;
else if (written == EOF)
return 0;
else
return written / size;
}
函数说明:fwrite用来把ptr指向的n个元素(每个元素占size个字节)写入到stream指向的文件流中去。
ptr:要写入的字符串的首地址
size:每个元素所占的大小
nmemb:元素的个数
stream:表示要写到那个文件流中去
返回值:成功返回实际写入到文件流中去的元素的个数 <=nmemb,失败返回-1,并且errno被设置。
冲洗流 fflush 同步
int fflush(FILE *stream);
stream:要同步的文件流
返回值:成功返回0,失败返回EOF(-1),同时errno被设置。
定位流
fseek
int fseek(FILE *stream, long offset, int whence);
stream:文件流指针,表示你要定位哪个未见
offset:偏移量 可正可负
whence:定位的方式,有如下三种
SEEK_SET:基于文件开头定位
新光标位置=文件开头+offset(>=0)
SEEK_CUR:基于当前光标位置定位
新光标位置=当前位置+offset(可正可负)
SEEK_END:基于文件末尾定位
新光标位置=文件末尾+offset(可正可负)
返回值:成功返回0,失败返回-1,并且errno被设置。
ftell
long ftell(FILE *stream);
函数说明:ftell返回当前光标位置离文件开头有多少个字节。
rewind
void rewind(FILE *stream);
函数说明:把文件光标定位在文件的开头。
rewind(stream)<===>fseek(stream,0,SEEK_SET);
格式化IO
scanf
int scanf(const char *format, ...);
老师笔记:
sscanf
int sscanf(const char *str, const char *format, ...);
老师笔记:
fscanf
int fscanf(FILE *stream, const char *format, ...);
老师笔记:
格式化输出:
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char
printf
老师笔记:
fprintf
老师笔记:
sprintf
老师笔记:还有总结