文件io简单了解

 一般在开发中,不会讲大量数据保存在代码中,而是存储在文件中,程序运行时,对文件的数据进行读取和写入,对硬件来说,操作系统也会将其看做文件,只不过需要操作对应的驱动文件来实现数据的写入和读取。

(1)文件io的分类:

一个是系统io、另一个是标准io

系统io:

标准io:

 系统io直接操作数据,标准io将数据打包给io缓冲区后再操作。

 (2)系统io和标准io的区别

首先都能够打开文件、读取文件、写入文件、关闭文件、文件相关操作

区别:函数名不同,头文件不同;

相同点:两种操作方式都适用于操作文件的;

不同点

系统io:在操作系统层面,由系统提供,可直接调用系统中的库的函数接口;

              不带io缓冲区的操作;

标准io:在库函数层面,有标准C库提供,可直接调用标准库函数的函数接口;

            带io缓冲区的操作;

关系:标准io函数,其本质也是调用系统io,只不过对系统io进一步进行封装;

(3)优缺点

系统io:由系统提供,有基本的操作函数。(简洁精练)

优点:简洁单一、稳定,不需要消耗过多的系统资源;

缺点:对功能性要求较高的程序,操作更加繁琐;

标准io:由标准C库提供,拥有多样化的操作函数(功能多样)

优点:复杂操作更为方便,函数接口更加多样化;

缺点:消耗系统资源更多,不适用于简单的功能;

2.Linux中一共存在七种文件类型

名称缩写符号创建命令代码打开方式代码关闭方式
普通文件-touchopen/fopenclose/fclose
目录文件dmkdiropendirclose
网络文件ssocketsocketclose
链接文件llnopen/fopenclose/fclose
管道文件pmkfifoopen/fopenclose/fclose
字符设备文件cmknod驱动的接口 openclose
块设备文件bmknod驱动的接口 open close

备注:设备文件可以被创建,但是如果没有对应的硬件或者驱动文件的支持,那么它只是单纯的占用内存,不起任何作用,也无法做任何操作。

3.文件描述符

 在操作文件的时候,并不是直接操作文件,而是给文件一个标号,或者叫引用对象,取名叫做文件描述符,在虚拟文件系统中,有一个文件类型的结构体,其中有一个fd array

1024是系统默认的文件操作安全范围,可以修改。

ulimit -n 更改之后的值

 文件描述符中,几个特殊的标号:

第一个下标为0的文件描述符,默认为键盘输入,对应的宏为STDIN;

第二个下标为1的文件描述符,默认为屏幕输出,对应的宏为STDOUT;

第三个下标为2的文件描述符,默认为屏幕输出,对应的宏为STDERR;

对应的函数分别为:scanf()   printf()   perror()

程序运行时,第一个打开的文件描述符号从3开始

4.相关的文件操作函数接口

系统函数 man 2,标准库函数 man 3

第一组:系统io操作函数

1.新建和打开文件的函数 open

该函数有两个功能:打开文件/创建文件

//头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//函数原型
int open(const char *pathname, int flags);

//函数参数
const char *pathname //文件对象的访问路径
int flags            //

O_RDONLY    //只读
O_WRONLY    //只写
O_RDWR      //读写权限
O_CREAT     //创建权限
O_EXCL      //和新建权限配合,如果文件存在就返回错误信息,用来检测文件是否存在
O_TRUNC     //和新建权限配合,如果文件存在就将文件原本内容全部丢弃,文件大小为0
O_NOCTTY    //默认
O_APPEND    //文件内容追加写入,主要用来写文件(保护之前的内容不被覆盖)

返回值:
    成功:返回一个新的文件描述符;
    失败:-1;

文件描述符:默认起始为3,一般最大申请1023,总数1024;

另一种open的创建

//函数原型
int open(const char *pathname, int flags, mode_t mode);

//函数参数
const char *pathname //文件对象的访问路径
int flags            //文件对象的操作权限
mode_t mode          //设置文件的使用权限(宏对应的八进制)

O_RDONLY    //只读
O_WRONLY    //只写
O_RDWR      //读写权限
O_CREAT     //创建权限
O_EXCL      //和新建权限配合,如果文件存在就返回错误信息,用来检测文件是否存在
O_TRUNC     //和新建权限配合,如果文件存在就将文件原本内容全部丢弃,文件大小为0
O_NOCTTY    //默认
O_APPEND    //文件内容追加写入,主要用来写文件(保护之前的内容不被覆盖)

返回值:
    成功:返回一个新的文件描述符;
    失败:-1;

文件描述符:默认起始为3,一般最大申请1023,总数1024;

相关宏(八进制)
创建者:
        S_IRWXU  00700 user  (file  owner) has read, write, and execute permission
        S_IRUSR  00400 user has read permission
        S_IWUSR  00200 user has write permission
        S_IXUSR  00100 user 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 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

2.读取文件内容 read

//头文件
#include <unistd.h>

//函数原型
ssize_t read(int fd, void *buf, size_t count);

//函数参数
int fd, 		//由open函数返回的文件描述符对象
void *buf 	    //读取数据存放的缓冲区
size_t count	//计算读取字节的大小(字节)想要读取的字节数

返回值:
    成功:返回实际读取的内容字节数
    失败:-1

3.将数据内容写入文件 write

//头文件
#include <unistd.h>

//函数原型
ssize_t write(int fd, const void *buf, size_t count);

//函数参数
int fd;        //需要写入的文件描述符
const void *buf;    //写入的数据来自该缓冲区
size_t count        //控制写入的字节数大小(字节)

返回值:
    成功:返回写入内容的字节数
    失败:-1

4.关闭文件函数 close

//头文件
#include <unistd.h>

//函数原型
int close(int fd);

//函数参数
int fd;        //需要关闭的文件对象

注意:如果创建了文件,并向文件中写入了数据,如果没有关闭close,则数据可能无法保存。

5.文件的偏移(光标)占位符的移动 lseek

//头文件
#include <sys/types.h>
#include <unistd.h>

//函数原型
off_t lseek(int fd, off_t offset,int whence)

//函数参数
int fd;        //文件操作对象(也就是生成的文件描述符)
off_t offset;  //要偏移的字节数
int whence;    //偏移的基准点

偏移的基准点:
    SEEK_SET    //文件的开头位置
    SEEK_CUR    //文件的当前位置
    SEEK_END    //文件的末位位置

返回值:
    成功:返回偏移过的字节数
    失败:-1

注意:可以让偏移的基准点配合,可以计算出文件内容的字节数大小

6.文件的属性 stat

//头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

//函数原型
int stat(const char *pathname, struct stat *buf);

//函数原型
const char *pathname;    //需要查看的文件的路径
struct stat *buf;        //文件属性的结构体指针

文件属性结构体成员(封装)
struct stat
{
    dev_t     st_dev;         /* 文件存储的设备号 */ 系统自动分配
    ino_t     st_ino;         /* 索引号 文件的身份证 */
    mode_t    st_mode;        /* 文件的类型和权限 */
    nlink_t   st_nlink;       /* 硬链接数 */
    uid_t     st_uid;         /* 创建者用户ID */
    gid_t     st_gid;         /* 同组用户ID */
    dev_t     st_rdev;        /* 特殊设备ID */ 驱动文件
    off_t     st_size;        /* 文件大小 */
    blksize_t st_blksize;     /* 文件所用数据块的数目 */
    blkcnt_t  st_blocks;      /* 写入数据建议值 */
    struct timespec st_atim;  /* 最后访问文件的时间 */
    struct timespec st_mtim;  /* 最后修改文件的时间 */
    struct timespec st_ctim;  /* 最后修改属性的时间 */
}

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

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 // 链接文件

S_ISSOCK(m) socket  // 网络文件

 应用方式:

stat(pathname, &sb);   // 先获得文件属性结构体指针

if (S_ISREG(sb.st_mode))

{        // 然后使用带参宏去判断成员st_mode,返回为真,则为普通文件

        /* Handle regular file */

}

 除了使用带参宏,还可以使用以下的内容去判断文件的类型

S_IFMT    0170000   bit mask for the file type bit field  总权限文件掩码

           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(pathname, &sb); // 先获得文件属性结构体

if ((sb.st_mode & S_IFMT) == S_IFREG)

{        // 做位与运算,如果结果和某一个宏相同即得到文件类型

        /* Handle regular file */

}

获取文件的权限:     

S_ISUID     04000   set-user-ID bit         // 创建者ID

           S_ISGID     02000   set-group-ID bit (see below) // 同组用户ID

           S_ISVTX     01000   sticky bit (see below) // 其他用户ID

创建者-这个组的权限

           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

 7.判断文件是否存在 access

//头文件
#include <unistd.h>

//函数原型
int access(const char *pathname, int mode)

//函数参数
const char *pathname;      //需要判断的文件的路径
int mode;                  //附加条件:R_OK 读权限 W_OK 写权限 X_OK 执行权限 F_OK 文件是否存在

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

8.文件重定向 dup 重定向文件描述符

//头文件
#include <unistd.h>

//函数原型
int dup(int oldfd);
int dup2(int oldfd, int newfd);

//函数参数
int oldfd;    //需要重定向的旧文件描述符
int newfd;    //指向新的重定向文件描述符

返回值:
    成功:返回新的文件描述符(说明已改变文件描述符指向)
    失败:返回-1

此外:
函数dup和dup2提供了复制文件描述符的功能。他们通常用于stdin,stdout或进程的stderr的重定向。一般来说,普通输出函数(如:printf),默认是将某信息写入到文件描述符为1的文件中,普通输入函数都默认从文件描述符为0的文件中读取数据。因此重定向操作实际上是关闭某个标准输入输出设备(文件描述符为0、1、2),而将另一个打开的普通文件的文件描述符设置为0、1、2.
   输入重定向:关闭标准输入设备,打开(或复制)某普通文件,使其文件描述符为0.
   输出重定向:关闭标准输出设备,打开(或复制)某普通文件,使其文件描述符为1.

 

第二组:标准io

1.打开函数 fopen

//头文件
#include <stdio.h>

//函数原型
FILE *fopen(const char *pathname, const char *mode);

//函数参数
const char *pathname;        //打开文件的路径
const char *mode;            //打开文件后文件以怎样的权限展示

附加:
    r	以只读的方式打开文件
	r+	以读写的方式打开文件
	w	以可写的方式打开文件,如果文件存在就清空,不存在就创建
	w+	以读写的方式打开文件,如果文件存在就清空,不存在就创建
	a	以可写的方式打开文件,如果存在就追加,不存在就创建,EOF符保留
	a+	以读写的方式打开文件,如果存在就追加,不存在就创建,EOF符不保留

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

具体可以参考大佬的fopen函数及读写权限

和另一位大佬的文件描述符与文件流指针以及重定向

 2.关闭函数 fclose

//头文件
#include <stdio.h>

//函数原型
int fclose(FILE *stream);

//函数参数
FILE *stream        //文件流指针

返回值:
    成功:返回0
    失败:返回EOF(头文件中EOF被定义为-1)

3.读写函数 fread fwrite

//头文件
#include <stdio.h>

//函数原型
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

//函数参数
void *ptr 	    //读写数据存储的缓冲区位置
size_t size, 	//每一块存储区的大小(字节)
size_t nmemb    //打算使用多少块操作数据(和calloc类似)
FILE *stream    //操作对象的文件流指针

返回值:
	成功:返回读取或者写入的完整的数据块的个数
	失败:返回0或者其他小于0的数据

注意:返回值是读取的完整的数据块的个数,就算最后一次读取的数据没有达到一个数据块的长度,数据还是会被读取

4.标准io文件属性 feof

//头文件
#include <stdio.h>

//函数原型1
void clearerr(FILE *stream); // 清除文件的错误码
int feof(FILE *stream); // 检查光标是否到了文件的末尾

返回值:
	成功:非零的时候就表示到达了文件的末尾
	失败:为零的时候就是没有找到文件末尾位置
       int ferror(FILE *stream); // 检查文件操作过程中的出错编码

返回值:
	成功:为零的时候说明没有错误
	失败:非零的时候,表示遇到错误
       int fileno(FILE *stream);	// 检查文件描述符是多少

=================================================================
//函数参数
FILE *stream    //文件流指针

feof的缺陷:就算文件光标移动到了文件末尾,那也需要再读一次,才能获得非零值

在C里,只有当文件位置指针到了文件末尾,然后再发生读/写操作时,标志位才会被置为含有_IOEOF。

5.标准io中文件偏移 fseek

//头文件
#include <stdio.h>

//函数原型
int fseek(FILE *stream, long offset, int whence);

//函数参数
FILE *stream 	//偏移对象,文件流指针
long offset 	//偏移量 字节
int whence		//基准点
    {
		SEEK_SET	文件的起始位置
		SEEK_CUR	文件的当前位置
		SEEK_END	文件的末尾位置
    }

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

注意:fseek与lseek返回值不相同

       long ftell(FILE *stream); // 返回当前文件位置,距离文件开头处的长度

注意:ftell只做计算,不做偏移

       void rewind(FILE *stream); 等价于(void) fseek(stream, 0L, SEEK_SET),将偏移量偏移到文件开头处

6.标准化的输入函数----字符和字符串

//头文件
#include <stdio.h>

//从文件里面获取一个字符
int fgetc(FILE *stream);

//从文件中读取size-1长度的字符串,存放到s为首地址的内存中,或者读到换行符时结束,
char *fgets(char *s, int size, FILE *stream); //返回值与s相同,都指向存储字符串的缓冲区,如果失败,返回NULL

//从文件中获取一个字节 (fgetc和getc的区别:fgetc是函数,getc是带参宏)
int getc(FILE *stream);

//获取缓冲区的一个字符
int getchar(void);

//获取缓冲区中的一个字符串
char *gets(char *s); // 因为经常会导致内存溢出,所以已经停用

==============================================================

函数参数:
	FILE *stream 文件流指针
	int size 	大小(字节)
	char *s	char *类型的指针,指向存储字符串的空间

7.标准化输出函数----字符和字符串

//头文件
#include <stdio.h>

//输出一个字符到文件中
int fputc(int c, FILE *stream);

//输出一个字符串到文件中
int fputs(const char *s, FILE *stream);

//输出一个字符到文件中,fputc是函数,putc是宏定义
int putc(int c, FILE *stream);

//输出一个字符
int putchar(int c);

// 输出一个字符串
int puts(const char *s);

函数参数:
	int c		    //字符或者ASCII码值
	const char *s   //字符串数据
	FILE *stream    //文件流指针

8.格式化输出----可变参函数

//头文件
#include <stdio.h>

//格式化输出到屏幕(stdout)上
int printf(const char *format, ...);

//格式化输出到文件中(标准IO)
int fprintf(FILE *stream, const char *format, ...);

//格式化输出到文件中(系统IO)
int dprintf(int fd, const char *format, ...);

//组合字符串,将格式化的组合之后的字符串输出到str指向的内存中
int sprintf(char *str, const char *format, ...);

//组合字符串,将格式化的组合之后的size-1个字符串输出到str指向的内存中
int snprintf(char *str, size_t size, const char *format, ...);

//函数参数
const char *format      //格式化符号 %d %s %c
FILE *stream	        //文件流指针
int fd		            //文件描述符
char *str		        //str存储字符串的位置
size_t size	            //输出大小

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为你介绍一下如何使用 Java 编写程序实现文件拷贝。 Java 提供了非常方便的 I/O 类来处理文件读写操作。文件拷贝的基本思路是读取源文件中的数据,然后将数据写入到目标文件中。下面是一个简单文件拷贝程序示例: ```java import java.io.*; public class FileCopy { public static void main(String[] args) throws IOException { // 源文件路径 String srcFilePath = "C:/temp/source.txt"; // 目标文件路径 String destFilePath = "C:/temp/dest.txt"; // 创建输入流和输出流 FileInputStream fis = new FileInputStream(srcFilePath); FileOutputStream fos = new FileOutputStream(destFilePath); // 定义缓冲区,每次读取 1024 个字节 byte[] buffer = new byte[1024]; int len; // 循环读取源文件中的数据,并将数据写入到目标文件中 while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } // 关闭输入流和输出流 fis.close(); fos.close(); System.out.println("文件拷贝完成!"); } } ``` 在上面的示例中,我们首先定义了源文件路径和目标文件路径。然后创建了输入流和输出流,用于读取源文件和向目标文件写入数据。我们定义了一个缓冲区,每次从输入流中读取 1024 个字节,并将这些字节写入到输出流中。最后,我们关闭输入流和输出流,并输出一条完成信息。 需要注意的是,上面的代码中使用了 try-catch 语句来捕获可能出现的 IOException 异常。这是因为在文件读写过程中可能会出现异常,比如文件不存在、文件无法读取等等。为了保证程序的健壮性,我们需要使用 try-catch 语句来处理这些异常。 希望这个简单的示例可以帮助你了解如何使用 Java 编写文件拷贝程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值