一、打开文件open
#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 //只写
O_RDWR //读写
//附加
O_APPEND //追加
O_CREAT //文件不存在 创建
O_TRUNC //文件存在 截短为0
@mode 一般不用
当flags中使用了 O_CREAT 时,需要指定 mode
mode 0777
0666
0444
返回值:
成功 文件描述符
失败 -1 && errno会被设置
cpp
#include<stdio.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
int fd = open(argv[1],O_WRONLY|O_CREAT,0666);
if(fd < 0)
{
perror("open fail");
return -1;
}
#if 0
printf("open succes fd = %d\n",fd);
printf("stdin fileno = %d\n",stdin->_fileno);
printf("stdout fileno = %d\n",stdout->_fileno);
printf("stderr fileno = %d\n",stderr->_fileno);
#endif
return 0;
}

对比fopen open
r O_RDONLY
r+ O_RDWR
w O_WRONLY|O_CREAT|O_TRUNC
w+ O_RDWR|O_CREAT|O_TRUNC
a O_WRONLY|O_APPEND|O_CREAT
a+ O_RDWR |O_APPEND|O_CREAT
文件创建好后的权限:
a.指定的mode
b.umask (掩码) //二进制位 --- 遮住
最终文件的权限 = ~umask & mode

二、关闭文件 close
int close(int fd);
功能:
关闭文件描述符
以便 再次使用
参数:
@fd 要关闭的文件描述符
返回值:
成功 0
失败 -1
文件描述符:
1.非负整型数值
2.分配原则
最小未使用
3.范围
0~1023
三、读写文件 read write
#include <unistd.h>
(一)ssize_t read(int fd, void *buf, size_t count);
功能:
从fd中读数据 ,存到 buf中
参数:
@fd 要读取的文件
@buf 存放读取到的数据的 内存空间
@count 一次要读取的数量(字节)
返回值:
成功 表示成功读到的字节数
失败 -1 && errno
读取结束:
0 表示到达文件结尾
(二)ssize_t write(int fd,const void *buf, size_t count);
功能:
把buf中 写到fd中
参数:
@fd 要写入的文件
@buf 存放数据的 内存空间
@count 一次要写入的数量(字节)
返回值:
成功 表示成功写入的字节数
失败 -1 && errno
cpp
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
char buf[1024] = {0};
while(1)
{
int ret = read(0,buf,100);
// buf[ret-1] = '\0';
// printf("ret = %d buf = %s\n",ret,buf);
write(1,buf,ret);
}
return 0;
}
练习:
cat //文件IO 实现
cpp
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
int fp = open(argv[1],O_RDONLY);
if(fp < 0)
{
perror("open fail");
return -1;
}
char buf[1024] = {0};
int ret = 0;
while(ret = read(fp,buf,sizeof(buf)-1) )
{
write(1,buf,ret);
}
close(fp);
return 0;
}
练习:
实现cp
cpp
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("Usage: %s <dest> <srt>\n",argv[0]);
return -1;
}
int fp1 = open(argv[1],O_RDONLY);
int fp2 = open(argv[2],O_WRONLY | O_CREAT|O_TRUNC ,0666);
if(fp1 < 0 || fp2 < 0)
{
perror("open fail");
return -1;
}
char buf[1024] = {0};
int ret = 0;
while(ret = read(fp1,buf,sizeof(buf)-1) )
{
write(fp2,buf,ret);
}
close(fp1);
close(fp2);
return 0;
}
四、偏移lseek
off_t lseek(int fd, off_t offset, int whence);
功能:
重新定位文件偏移量
参数:
@fd 要定位的文件
@offset 偏移量
@whence 参考点
SEEK_SET //相对于文件开头的
offset >=0
SEEK_CUR //相对于当前位置
offset>=0
offset<0 //不能超过这个文件开头
SEEK_END //相对于文件末尾
offset < 0 //不能超过这个文件开头
offset >= 0 //可以 --- 创建 空洞 文件
返回值:
成功 从头的偏移量
失败 -1 && errno
lseek(fd,0,SEEK_SET); //定位到文件开头
lseek(fd,0,SEEK_END);
获得文件长度:
off_t len = lseek(fd,0,SEEK_END); //off_t <=> long int
注意:
1、不支持O_APPEND的追加模式,无法生成空洞文件。
2、lseek函数执行失败,文件指针还在偏移前的位置。
3、lseek函数在设备文件上偏移无效。 /dev/video0
4、fifo,socket 也不支持lseek的操作。
cpp
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
int fd = open(argv[1],O_RDONLY);
if(fd < 0)
{
perror("open fail");
return -1;
}
printf("open success fd = %d\n",fd);
printf("len = %ld\n",lseek(fd,0,SEEK_END));
return 0;
}
练习:
创建空洞文件
给定一个文件,创建一个同样大小的空洞文件
cpp
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
int fd1 = open(argv[1],O_RDONLY);
if(fd1 < 0)
{
perror("open fail");
return -1;
}
int len = lseek(fd1,0,SEEK_END);
int fd2 = open("hole.txt",O_WRONLY|O_CREAT,0666);
if(fd2 < 0)
{
perror("open fail");
return -1;
}
lseek(fd2,len-1,SEEK_END);//留出写'\0'的空间
write(fd2,"",1);//该字符串指向字符串常量区,空字符串里有'\0'
close(fd1);
close(fd2);
return 0;
}
五、库函数和系统调用对比
库函数: //标准IO
优点:
a.方便,功能多
b.可移植性好
标准
不足:
c.可能存在 安全性 隐患
系统调用:
优点:
a.使用起来,简单 ,功能简单
b.安全性高
c.设备文件 ---- 文件IO ///dev/video0 ----> fopen();
缺点:
c.很多复杂功能需要自己封装设计
d.可移植性差
六、系统调用中的函数
(一)fdopen() //将fd 转换 成 FILE * ------ fileno() //将FILE *转换 为 fd
1、FILE *fdopen(int fd,const char *mode);
功能:
将fd 转换 成 FILE *
参数:
@fd 要操作fd
@mode 打开的模式
返回值:
成功 FILE *
失败 NULL
2、int fileno(FILE *stream);
功能:
将FILE *转换 为 fd
参数:
@stream 要转换流指针
返回值:
成功 fd
失败 - && errno
cpp
#include<stdio.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
int fd = open("1.txt",O_RDONLY);
if(fd < 0)
{
perror("open fail");
return -1;
}
printf("fd = %d\n",fd);
FILE *fp = fdopen(fd,"r");
if(fp == NULL)
{
perror("fdopen fail");
return -1;
}
printf("success\n");
int fd1 = fileno(fp);
printf("fileno = %d\n",fd1);
return 0;
}
3、使用的选择:
标准IO --- 可移植性 如果操作只是文本数据
文件IO --- 安全性 操作的是硬件 ---文件IO
操作目录
目录也是一种文件
类似 标准IO
(二)opendir
DIR *opendir(const char *name);
功能:
打开目录,关联一个目录流
参数:
@name 要打开的目录名
返回值:
成功 返回DIR *
失败 NULL & errno
cpp
#include<stdio.h>
#include<dirent.h>
int main(int argc, const char *argv[])
{
DIR *dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir fail");
return -1;
}
printf("success\n");
return 0;
}
(三)读取目录readdir
struct dirent *readdir(DIR *dirp);
功能:
读取目录项
参数:
@dirp 目录流指针
返回值:
成功 struct dirent *
失败 NULL && errno
struct dirent
{
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen;/* Length of this record */
unsigned char d_type;/* Type of file; not supported by all filesystem types */
char d_name[256]; /* Null-terminated filename */
}
注意:
读到文件末尾 NULL
cpp
#include<stdio.h>
#include<dirent.h>
int main(int argc, const char *argv[])
{
DIR *dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir fail");
return -1;
}
printf("success\n");
struct dirent *pdir = readdir(dir);
printf("ino = %ld\n",pdir->d_ino);
printf("type = %d\n",pdir->d_type);
printf("name = %s\n",pdir->d_name);
closedir(dir);
return 0;
}
(四)close
int closedir(DIR *dirp);
功能:
关闭目录流
参数:
@dirp --- 目录流指针
返回值:
成功 0
失败 -1 &&errno
练习:
实现ls
cpp
#include<stdio.h>
#include<dirent.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <dirname>\n",argv[0]);
return -1;
}
DIR *dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir fail");
return -1;
}
struct dirent *pdir ;
while((pdir = readdir(dir)) != NULL)
{
if(pdir->d_name[0] != '.')
printf("%s ",pdir->d_name);
}
putchar('\n');
closedir(dir);
return 0;
}
练习:
shell
给定一个目录,统计目录下的 普通文件 和 目录文件的个数
cpp
#include<stdio.h>
#include<dirent.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <dirname>\n",argv[0]);
return -1;
}
DIR *dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir fail");
return -1;
}
struct dirent *pdir ;
int n = 0;
while((pdir = readdir(dir)) != NULL)
{
if(pdir->d_type == DT_REG)
{
n++;
}
}
rewinddir(dir);
int m = 0;
while((pdir = readdir(dir)) != NULL)
{
if(pdir->d_type == DT_DIR)
{
m++;
}
}
printf("directory = %d\n",m);
printf("regular = %d\n",n);
closedir(dir);
return 0;
}
(五)chdir
chdir ("/home/linux"); "../../"
fopen("1.mp4")
int chdir(const char *path);// /home/linux
功能:
改变当前程序的工作路径
参数:
path:改变到的路径
返回值:
成功 返回0
失败 返回-1
(六)getcwd
char *getcwd(char *buf, size_t size);
功能:
获得当前的工作路径
参数:
buf:保存工作路径空间的首地址
size:保存路径空间的长度
返回值:
成功返回包含路径空间的字符串首地址
失败返回NULL
cpp
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
char buf[1024];
getcwd(buf,sizeof(buf));
printf("cwd = %s\n",buf);
chdir("/home");
getcwd(buf,sizeof(buf));
printf("cwd = %s\n",buf);
return 0;
}
(七)mkdir
nt mkdir(const char *pathname, mode_t mode);//777 666 --x--x--x
功能:
创建一个目录
666-
参数:
pathname:路径
mode:
mode & ~umask 0002
返回值:
成功 返回0
失败 返回-1
(八)rmdir
int rmdir(const char *pathname);
功能:
删除一个空目录文件
参数:
pathname:目录文件的名字
返回值:
成功 返回0
失败 返回-1
cpp
#include<stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <dirname>",argv[0]);
return -1;
}
#if 0
if(-1 == mkdir(argv[1],0777))
{
printf("mkdir fail\n");
}else
{
printf("mkdir success\n");
}
#endif
if(-1 == rmdir(argv[1]))
{
printf("rmdir fail\n");
}else
{
printf("rmdir success\n");
}
return 0;
}
(九)sprintf
将格式化的输出结果,放到一个 字符数组空间上 形成一个字符串
cpp
#include<stdio.h>
int main(int argc, const char *argv[])
{
#if 0
int a = 10;
float b = 2.1;
char c = 'A';
printf("a = %d b = %f c = %c\n",a,b,c);
char buf[1024];
sprintf(buf,"%d %f %c",a,b,c);
printf("buf = %s\n",buf);
#endif
char buf[1024];
sprintf(buf,"%s/%s",argv[1],argv[2]);
printf("buf = %s\n",buf);
return 0;
}

(十)stat
int stat(const char *pathname, struct stat *statbuf);
功能:
获得文件的状态信息
参数:
@pathname //需要一个文件名字
@statbuf //用来保存文件的属性信息
返回值:
成功 0
失败 -1
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
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 */
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 */
};
cpp
O 对应的 3 个 bit 位用于描述其它用户的权限;
G 对应的 3 个 bit 位用于描述同组用户的权限;
U 对应的 3 个 bit 位用于描述文件所有者的权限;
S 对应的 3 个 bit 位用于描述文件的特殊权限。
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
利用宏判断文件类型
cpp
S_IFSOCK 0140000 socket(套接字文件)
S_IFLNK 0120000 symbolic link(链接文件)
S_IFREG 0100000 regular file(普通文件)
S_IFBLK 0060000 block device(块设备文件)
S_IFDIR 0040000 directory(目录)
S_IFCHR 0020000 character device(字符设备文件)
S_IFIFO 0010000 FIFO(管道文件)
实现stat
cpp
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <file name>\n",argv[1]);
return -1;
}
struct stat st;
if(lstat(argv[1],&st) < 0)
{
perror("stat fail");
return -1;
}
printf("File: %s\n",argv[1]);
printf("Size: %ld\t",st.st_size);
printf("Blocks: %ld\t",st.st_blocks);
printf("IO Blocks: %ld\t",st.st_blksize);
char type = 0;
switch (st.st_mode & S_IFMT)
{
case S_IFREG:
type = '-';
printf(" Regular file\n"); break;
case S_IFDIR:
type = 'd';
printf(" Directory\n"); break;
case S_IFCHR:
type = 'c';
printf(" Character device\n"); break;
case S_IFBLK:
type = 'b';
printf(" Block device\n"); break;
case S_IFIFO:
type = 'p';
printf(" FIFO/pipe\n"); break;
case S_IFLNK:
type = 'l';
printf(" Symbolic link\n"); break;
case S_IFSOCK:
type = 's';
printf(" Socket\n"); break;
default:
printf(" Unknown\n"); break;
}
printf("Device: %ldd\t",st.st_dev);
printf("Inode: %ld\t",st.st_ino);
printf("Links = %ld\n",st.st_nlink);
printf("Access: (-");
printf("%#o/%c",st.st_mode&0777,type);
printf((st.st_mode & S_IRUSR) ? "r" : "-");
printf((st.st_mode & S_IWUSR) ? "w" : "-");
printf((st.st_mode & S_IXUSR) ? "x" : "-");
printf((st.st_mode & S_IRGRP) ? "r" : "-");
printf((st.st_mode & S_IWGRP) ? "w" : "-");
printf((st.st_mode & S_IXGRP) ? "x" : "-");
printf((st.st_mode & S_IROTH) ? "r" : "-");
printf((st.st_mode & S_IWOTH) ? "w" : "-");
printf((st.st_mode & S_IXOTH) ? "x" : "-");
printf("-)\t");
printf("Uid: (%d / %s)\t",st.st_uid,getpwuid(st.st_uid)->pw_name);
printf("Gid: (%d / %s)\n",st.st_gid,getgrgid(st.st_gid)->gr_name);
struct tm *ptm = localtime(&st.st_atime);
printf("Access: %04d-%02d- %2d:%02d:%02d:%2d \n",
ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,
ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
ptm = localtime(&st.st_mtime);
printf("Modify: %04d-%02d- %2d:%02d:%02d:%2d \n",
ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,
ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
ptm = localtime(&st.st_ctime);
printf("Change: %04d-%02d- %2d:%02d:%02d:%2d \n",
ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,
ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
putchar('\n');
return 0;
}
(十一)getpwuid
struct passwd *getpwuid(uid_t uid);
功能:
根据用户id到/etc/passwd文件下解析获得
结构体信息
参数:
uid:用户id
返回值:
成功返回id对应用户的信息
失败返回NULL
(十二)getpwnam
struct passwd *getpwnam(const char *name);
功能:
根据用户名到/etc/passwd文件下解析获得
结构体信息
cpp
struct passwd
{
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
//提取/etc/group中的信息
/etc/group
getgrgid gid==>struct group *
getgrnam name==>struct group *
(十三)getgrgid
struct group *getgrgid(gid_t gid);
功能:
根据gid到/etc/group文件中解析组信息
参数:
gid:组id
返回值:
成功返回组信息
失败返回NULL
(十四)struct group *getgrnam(const char *name);
功能:
根据组名到/etc/group文件中解析组信息
cpp
struct group
{
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
char ** == &char*
gr_mem == &gr_mem[0]
char *
gr_mem[0] -> "zhangsan"
gr_mem[1] -> "lisi"
gr_mem[2] -> NULL
(十五)symlink
int symlink(const char *oldpath, const char *newpath);
功能:
创建一个链接向oldpath文件的新符号链接文件
参数:
oldpath:被链接向的文件的路径
newpath:新符号链接文件
返回值:
成功返回0
失败返回-1
(十六)link
int link(const char *oldpath, const char *newpath);
功能:
创建一个硬链接文件
参数:
oldpath:要链接向的文件
newpath:创建的新硬链接文件
返回值:
成功返回0
失败返回-1
(十七)remove
//rm --- rmdir
int remove(const char *pathname);
功能:
删除一个文件
参数:
pathname:删除文件的路径
返回值:
成功返回0
失败返回-1
(十八)rename
int rename(const char *oldpath, const char *newpath);
功能:
将一个老的路径名改为新的路径
参数:
oldpath:老路径名
newpath:新路径名
返回值:
成功 返回0
失败 返回-1
(十九)ftruncate
原型
cpp
#include <unistd.h>
int ftruncate(int fd, off_t length);
功能:
ftruncate
系统调用用于改变一个已打开文件的长度。如果文件的当前大小大于指定的长度,则超出部分的内容将被丢弃。如果文件的当前大小小于指定的长度,则文件的长度将被增加,新增加的部分将读为零字节
-
参数:
fd
:要修改长度的文件的文件描述符。length
:文件的新长度,以字节为单位。
-
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
以指示错误类型。常见的错误包括EBADF
(无效的文件描述符)、EINVAL
(无效的长度值,比如负值)以及EACCES
(权限不足)
这将尝试将 example.txt
文件的长度截断为100字节。
cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <filename> <new_length>\n", argv[0]);
return 1;
}
const char *filename = argv[1];
off_t new_length = strtol(argv[2], NULL, 10);
// 打开文件
int fd = open(filename, O_RDWR);
if (fd == -1) {
perror("open");
return 1;
}
// 截断文件
if (ftruncate(fd, new_length) == -1) {
perror("ftruncate");
close(fd);
return 1;
}
// 关闭文件
if (close(fd) == -1) {
perror("close");
return 1;
}
printf("File %s truncated to %lld bytes.\n", filename, (long long)new_length);
return 0;
}
出错相关函数接口
(二十)error
void error(int status, int errnum, const char *format, ...);
功能:
程序出错打印对应出错原因和用户输入字符串并退出
参数:
status:程序退出的状态
EXIT_FAILURE 1
EXIT_SUCCESS 0
errnum:错误码
format:
类似printf打印
返回值:
缺省
FILE 表示是那个文件
LINE 表示第几行
func 表示在那个函数
DATE
cpp
#include<stdio.h>
#include<errno.h>
#include<error.h>
int main(int argc, const char *argv[])
{
int n =0;
scanf("%d",&n);
errno = n;
if(n > 0)
{
error(0,n,"test error = %d\n",123);
}
return 0;
}
(二十一)strerror
char *strerror(int errnum);
功能:
打印errnum出错码对应的出错信息
参数:
errnum:出错errno号
返回值:
成功返回对应的错误信息
ruturn 1;
cpp
#include<stdio.h>
#include<errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int n =0;
scanf("%d",&n);
errno = n;
printf("test error str: %s\n",strerror(errno));
return 0;
}

缺省
FILE__ 表示是那个文件
LINE 表示第几行
func 表示在那个函数
DATE
cpp
#include<stdio.h>
int main(int argc, const char *argv[])
{
printf("%s:%d:%s:%s\n",__FILE__,__LINE__,__func__,__DATE__);
return 0;
}