《Linux C编程实战》笔记:文件读写

Linux c下文件读写可用creat,open,close,read,write,lseek等函数。对于跨平台的程序还是用C标准库的fopen等。

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);

第一个参数是文件名,第二个参数是打开文件的方式

flags:

  1. O_RDONLY: 只读打开文件。
    以只读方式打开文件。

  2. O_WRONLY: 只写打开文件。
    以只写方式打开文件。

  3. O_RDWR: 读写方式打开文件。
    以读写方式打开文件。

  4. O_CREAT: 如果文件不存在,则创建文件。
    如果文件不存在,则创建文件。

  5. O_EXCL: 与O_CREAT一起使用,用于确保文件是新创建的。如果文件已存在且O_EXCL标志被指定,则open会失败。

  6. O_TRUNC: 如果文件存在且以写方式打开,则将文件截断为零长度。

  7. O_APPEND: 在文件末尾追加数据。

  8. O_NONBLOCK: 非阻塞模式。使用此标志,文件描述符将以非阻塞方式打开,读写操作不会阻塞进程。

  9. O_SYNC: 打开文件以同步写入方式。所有写入操作将立即写入物理介质。

前三个方式互斥,但可以与后面的进行或运算

mode 参数用于指定新文件的权限,它是一个三位的八进制数字,每一位表示不同的权限。这个参数通常与 O_CREAT 标志一起使用,以确保新文件被创建时有适当的权限设置。

以下是一些常见的权限位和它们的含义:

  1. S_IRUSR (0400): 用户(文件所有者)具有读权限。 中文:用户(文件所有者)具有读权限。

  2. S_IWUSR (0200): 用户具有写权限。

  3. S_IXUSR (0100): 用户具有执行权限。 

  4. S_IRGRP (0040): 组具有读权限。 

  5. S_IWGRP (0020): 组具有写权限。

  6. S_IXGRP (0010): 组具有执行权限。 

  7. S_IROTH (0004): 其他用户具有读权限。

  8. S_IWOTH (0002): 其他用户具有写权限。

  9. S_IXOTH (0001): 其他用户具有执行权限。

这些权限位可以通过按位或运算组合在一起,以设置文件的权限。例如,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 表示文件所有者具有读写权限,而组和其他用户只有读权限。

mode 参数中,这些权限位可以使用宏来表示,如 S_IRWXU 表示用户有读、写和执行权限。这有助于提高代码的可读性。

成功调用open会返回一个文件描述符,若有错误则返回-1,并把错位代码赋给errno。

creat

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat (const char *pathname, mode t mode);

creat 系统调用用于以只写方式打开文件,并在文件不存在时创建它。它相当于使用 open 调用,其中包括了 O_WRONLY | O_CREAT | O_TRUNC 标志。

  • pathname:包含文件路径的字符串。
  • mode:文件权限位(类似于 open 系统调用中的 mode 参数)。

返回值

成功时,creat 返回一个非负整数,它是与新创建或已打开文件关联的文件描述符。失败时返回-1,并设置 errno 以指示错误。

close

#include <unistd.h>
int close(int fd);

close 函数关闭一个先前由 opencreat 打开的文件描述符。关闭文件描述符后,它将不再指向任何文件,且不能再用于读取或写入。

  • fd:要关闭的文件描述符。

示例程序1

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<cstdlib>
int main(){
    int fd;
    //打开文件,文件不存在自动创建,文件存在则出错,文件所有者有读写权限
    if((fd=open("example_62.c",O_CREAT|O_EXCL,S_IRUSR|S_IWUSR))==-1){
        perror("open");
        //printf("open:%s  with errno:%d\n",strerror(errno),errno);
        exit(1);
    }else{
        printf("create file success\n");
    }
    close(fd);
    return 0;
}

第一次运行

成功创建。再一次运行呢?

错误,因为O_EXCL被设置。

顺便,了解一下上面头文件的作用

  1. sys/types.h

    • 包含各种基本数据类型的定义,如 size_tpid_t
    • 通常用于定义与系统相关的数据类型。
  2. sys/stat.h

    • 包含文件状态信息的结构体定义,如 struct stat
    • 提供了文件权限位的宏定义,如 S_IRUSRS_IWGRP
    • 用于获取和设置文件的状态信息。
  3. fcntl.h

    • 定义了文件控制操作所需的常量,如 O_RDONLYO_WRONLY
    • 提供了 open 函数和一些文件控制操作函数的声明。
    • 用于打开、关闭、复制和其他文件操作。
  4. unistd.h

    • 提供了 POSIX 系统调用的声明,如 readwriteclose
    • 包含一些常用的符号常量,如 STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO
    • 用于实现对文件描述符的基本 I/O 操作。
  5. errno.h

    • 定义了 errno 全局变量,该变量在系统调用失败时被设置为指示错误的整数值。
    • 包含一些与错误码相关的宏定义,如 EACCESEINVAL
    • 用于检查和处理系统调用产生的错误。

这些头文件通常用于系统级编程,允许开发者使用系统调用和低级 I/O 操作。在编写需要直接与文件系统、进程控制等打交道的程序时,这些头文件是必需的。

read

#include <unistd.h>
ssize_t read(int fd, void *buf, sizet count);
  1. fd (file descriptor)

    • 文件描述符,它是一个非负整数,用于标识已打开文件或 I/O 设备。通常由 open 函数返回。
    • 表示从哪个文件或设备读取数据。
  2. buf (buffer)

    • 指向用于存放读取数据的缓冲区的指针。
    • buf 必须是一个有效的内存地址,足够大以容纳要读取的数据。
  3. count (size_t)

    • 期望读取的字节数。
    • count 参数指定了 buf 缓冲区的大小,即期望从文件中读取的字节数。
  4. 返回值 (ssize_t)

    • 返回值是已经成功读取的字节数。如果到达文件末尾(EOF),返回值为 0。
    • 如果出现错误,返回值为 -1,并且 errno 被设置以指示错误的原因。

最好能将返回值与参数count进行比较,若比count少,则可能是读到文件尾或读取被中断。

读写指针会跟着移动

wtire

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  1. fd (file descriptor):

    • 文件描述符,是一个非负整数,用于标识已打开文件或 I/O 设备。通常由 open 函数返回。
    • 表示写入数据的目标文件或设备。
  2. buf (buffer):

    • 指向包含待写入数据的缓冲区的指针。
    • buf 必须是一个有效的内存地址,包含要写入的数据。
  3. count (size_t):

    • 要写入的字节数。
    • count 参数指定了 buf 缓冲区中的数据大小,即要写入的字节数。
  4. 返回值 (ssize_t):

    • 返回已经成功写入的字节数。如果写入失败,返回值为 -1,并且 errno 被设置以指示错误的原因。

lseek

用来移动文件读写指针的位置

#include<sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  1. fd (file descriptor):

    • 文件描述符,是一个非负整数,用于标识已打开文件或 I/O 设备。通常由 open 函数返回。
    • 表示要操作的文件。
  2. offset (off_t):

    • 偏移量,是一个有符号整数,指定了文件偏移的目标位置。
    • 如果 whence 参数是 SEEK_SET,则 offset 表示从文件开始处的偏移量;如果是 SEEK_CUR,则 offset 表示从当前文件偏移处的偏移量;如果是 SEEK_END,则 offset 表示从文件末尾处的偏移量。
  3. whence (int):

    • 定位基准,指定了偏移量的计算方式。可以是 SEEK_SETSEEK_CURSEEK_END
    • SEEK_SET 表示从文件开始处计算偏移,SEEK_CUR 表示从当前位置计算偏移,SEEK_END 表示从文件末尾处计算偏移。
  4. 返回值 (off_t):

    • 返回新的读写指针距离文件开始处的偏移量。如果出现错误,返回值为 -1,并且 errno 被设置以指示错误的原因。

示例程序2

演示文件读写和文件读写指针的移动操作过程

#include<iostream>
#include<cstring>
#include <cstdio>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
//自定义的错误处理函数
void my_err(const char *err_string,int line){
    fprintf(stderr,"line:%d ",line);
    perror(err_string);
    exit(1);
}
//自定义的读数据函数
int my_read(int fd){
    int len;
    int ret;
    int i;
    char read_buf[64];
    //获得文件长度
    if(lseek(fd,0,SEEK_END)==-1)//首先偏移指针移动到文件末尾
        my_err("lseek",__LINE__);
    if((len=lseek(fd,0,SEEK_CUR))==-1)//函数返回距离开头的偏移量大小,因为现在指针已经在末尾,SEEK_CUR==SEEK_END,这样函数返回的偏移量就是文件的长度
        my_err("lseek",__LINE__);
    if((lseek(fd,0,SEEK_SET))==-1)//把偏移指针回到开头,不会影响后续的读写
        my_err("lseek",__LINE__);
    printf("len:%d\n",len);
    //读
    if((ret=read(fd,read_buf,len))<0)
        my_err("read",__LINE__);//读操作出错
    for(i=0;i<len;i++)
        printf("%c",read_buf[i]);//打印读取的内容
    printf("\n");
    return ret;
}
int main(){
    int fd;
    char write_buf[32]="hello world";
    //可读可写方式创建,若文件存在则覆盖
    if((fd=open("example_63.c",O_RDWR|O_CREAT|O_TRUNC,S_IRWXU))==-1)//先打开文件,获得文件描述符
        my_err("open",__LINE__);
    else printf("create file success\n");
    //写
    if(write(fd,write_buf,strlen(write_buf))!=strlen(write_buf))//判断写是否完全成功
        my_err("write",__LINE__);
    my_read(fd);//再读

    printf("/*------------*/\n");
    if(lseek(fd,10,SEEK_END)==-1)//往文件末尾再向后移动10字节
        my_err("lseek",__LINE__);
    if(write(fd,write_buf,strlen(write_buf))!=strlen(write_buf))//在文件末尾后10字节处开始再写
        my_err("write",__LINE__);
    my_read(fd);//读
    close(fd);
    return 0;
}

运行结果

但是不完全对,运行下面命令发现

具体解释而言

if(lseek(fd,10,SEEK_END)==-1)//往文件末尾再向后移动10字节
        my_err("lseek",__LINE__);

文件读写指针移动到EOF后10字节再写,中间的间隔以'\0'填充,但是'\0'在C语言打印为空,所以程序打印的结果和查看文件内容的结果不尽相同。

顺便了解

od 命令是一个用于显示文件内容的 Linux/Unix 命令。它以八进制或十六进制的形式显示文件的内容,也可以按字节、短整数、长整数等格式显示。

以下是 od 命令的基本语法:

od [options] [file]

其中,file 是要查看内容的文件名。

一些常用的选项包括:

  • -a:以字符和八进制的形式显示文件内容。
  • -t:指定显示的格式,比如 -to2 表示以八进制短整数的形式显示。
  • -x:以十六进制形式显示文件内容。
  • -c:以字符形式显示文件内容。

perror 函数是一个用于打印与当前 errno 值相关联的错误消息的标准C库函数。它的原型如下:

#include <stdio.h>

void perror(const char *s);

其中:

  • s 是一个用户自定义的字符串,用于在错误消息前输出一些额外的信息。通常,这个字符串是简短的描述性文字,以帮助理解错误的上下文。

perror 函数的作用是将 errno 的当前值转换为对应的错误消息,并将该消息输出到标准错误流(stderr)。perror 在输出错误消息后,会加上冒号和空格,然后输出 s 指定的字符串,最后再输出一个换行符。

比如

#include <stdio.h>
#include <errno.h>

int main() {
    FILE *file = fopen("nonexistent_file.txt", "r");

    if (file == NULL) {
        perror("Error opening file");
    }

    return 0;
}

在上面的例子中,如果 fopen 失败(比如因为文件不存在),perror 将打印类似于以下的消息:

Error opening file: No such file or directory

fprintf(stderr, "line:%d ", line);

这行代码使用了 fprintf 函数来将格式化的输出发送到标准错误流(stderr)。下面是对该代码片段的解释:

  • fprintf 函数

    • fprintf 是标准C库提供的函数,用于将格式化的数据写入流中。
    • 它的原型为:int fprintf(FILE *stream, const char *format, ...);
    • stream 参数指定要写入的流,可以是 stdout(标准输出)或 stderr(标准错误)等。
    • format 参数是一个字符串,指定了输出的格式,类似于 printf 中的格式字符串。
    • 后续的参数是要插入到格式字符串中的值。
  • stderr

    • stderr 是标准错误流,与 stdout(标准输出流)类似,但通常用于输出错误信息。
    • 在C语言中,stderr 是一个预定义的文件指针,表示指向标准错误流的指针。
    • 输出到 stderr 通常用于报告程序运行中的错误和警告,而不是正常的输出。

fprintf(stderr, "line:%d ", line); 会将输出发送到标准错误流 (stderr),而在命令行中运行程序时,这些错误消息会在命令行终端上显示。

在Linux或Unix系统中,标准输出 (stdout) 通常与命令行终端关联,而标准错误 (stderr) 也会显示在相同的终端上。这意味着通过 fprintf(stderr, ...) 输出的内容将显示在运行程序的命令行终端上,而不是被重定向到文件或其他位置。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值