linuxC系统编程学习随手笔记(1) IO

还可以参考这些内容

<<UNIX环境高级编程第三版>>

Linux系统编程-文件IO - 骆三疯 - 博客园 (cnblogs.com)

Linux 安装中文 man 手册_linux man中文手册-CSDN博客

程序各个段text,data,bss,stack,heap - James110 - 博客园 (cnblogs.com)

C/C++程序存储区_c++ 哪些会存储在静态存储区-CSDN博客

C语言fgetc和fputc函数用法详解(以字符形式读写文件)_c语言库函数fgetc(fp)的功能是( ) 从文件fp中读取第一个字符 向屏幕上输出一个字-CSDN博客

Linux文件页、脏页、匿名页-CSDN博客

C标准库的文件IO函数----------

  • fopen、fclose、fseek、fgets、fputs、fread、fwrite......
  • 在命令行,通过 man fopen...... 等可以查看系统定义的对应的标库函数

fopen打开文件


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

int main(void)
{
    FILE *fp;
    fp = fopen("tem","r");
    if(fp == NULL)
    {
        fprintf(stderr,"fopen() failed err = %d\n",errno);
        exit(1);
    }
    puts("ok");
    exit(0);
}

找不到文件的时候,返回了err = 2;

打开errno-bash.h可以看到刚好对应

这样查看报错显然太慢了

方法1使用函数perror

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

int main(void)
{
    FILE *fp;
    fp = fopen("tem","r");
    if(fp == NULL)
    {
        //fprintf(stderr,"fopen() failed err = %d\n",errno);
        perror("fopen()"); //自动关联全局变量errno
        exit(1);
    }
    puts("ok");
    exit(0);
}

结果

方法2

strerror()

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

int main(void)
{
    FILE *fp;
    fp = fopen("tem","r");
    if(fp == NULL)
    {
        //fprintf(stderr,"fopen() failed err = %d\n",errno);
        //perror("fopen()"); //自动关联全局宏定义errno
        fprintf(stderr,"fopen:%s\n",strerror(errno));
        exit(1);
    }
    puts("ok");
    exit(0);
}


栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
堆区(heap) : 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
全局区(静态存储区)(static): 全局变量和静态变量的存储是放在一块的,a .初始化的全局变量和静态变量在一块区域(.data段), b .未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss段)。程序结束后有系统释放
文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放
代码区—存放函数体的二进制代码。
我们将文字常量区和代码区视为一个部分,即代码区

其中fopen返回的指针是放在堆上的。、

有打开就有关闭

fclose关闭文件

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    FILE *fp;
    fp = fopen("teiiiim","r");
    if(fp == NULL)
    {
        //fprintf(stderr,"fopen() failed err = %d\n",errno);
        perror("fopen()"); //自动关联全局宏定义errno
        //fprintf(stderr,"fopen:%s\n",strerror(errno));
        exit(1);
    }
    puts("ok");
    fclose(fp);
    exit(0);
}

ps: 解决vscode报错: 命令行错误: 指定的语言模式不兼容, c/c++:1027-CSDN博客

这里改个报错,命令行错误: 指定的语言模式不兼容, c/c++:1027


循环打开文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    int i = 0;
    FILE *fp;
    while(1)
    {
        fp = fopen("tem","w");
        if(fp == NULL)
        {
           
            perror("fopen()"); //自动关联全局宏定义errno
             break;
            //exit(1);
        }
        else
        {
            i++;
        }
    }
    printf("%d\n",i);
    puts("ok");
    exit(0);
}

结果

输入ulimit -a 查看最多可以打开的文件限制,当然这里是可以改的,这里可以看到为1048576 

但是我的数值却是1048559 少的17去哪里了?这里和操作环境有关,这里还打开了其他的文件流

按字符读写 fgetc、fputc

int fgetc (FILE *fp);

1,fp为文件指针 ,fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF

2,在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。

注意:这个文件内部的位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是隐藏的。

EOF 不绝对是 -1,也可以是其他负数,这要看编译器的实现。


fputc 是 file output char 的所以,意思是向指定的文件中写入一个字

int fputc ( int ch, FILE *fp );

把字符U写入*fp中

fputc('a', fp);

下面应用实现一个简单的mycopy函数,实现文件的复制

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

int main(int argc,char **argv)
{

    if(argc < 3) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
        exit(1);
    }
    FILE *fps,*fpd;
    int ch;
    fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写  
    if(fps == NULL)
    {
        fclose(fps);
        perror("fopen");
        exit(1);
    }
    fpd = fopen(argv[2],"w");
    if(fpd == NULL)
    {
        fclose(fpd);
        perror("fopen");
        exit(1);
    }
    while(1)
    {
        ch = fgetc(fps);
        if(ch == EOF) //读到文件尾的话
            break;
        fputc(ch,fpd); //把内容写到fpd中
    }

    fclose(fpd);
    fclose(fps);
    exit(0);
}

执行结果

可以看到生成了一个和list2.h一样的7777.h文件

文本字符数量计数小练习

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

int main(int argc,char **argv)
{
    FILE *fp = NULL;
    int count = 0;
    if(argc < 2) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file>\n>",argv[0]);
        exit(1);
    }
    fp = fopen(argv[1],"r"); //打开文件 argv[1] 只写  
    if(fp == NULL)
    {
        fclose(fp);
        perror("fopen");
        exit(1);
    }
    while(fgetc(fp) != EOF)
    {
        count++;
    }
    printf("%d\n",count);
    exit(0);
}

fgets和fputs

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

fgets 读到 size -1 或者\n 会结束读取

#include <stdio.h>
#include <stdlib.h>
#define SIZE 5
char buf[SIZE];
fgets(buf,SIZE,stream);

读取 abcdef
SIZE = 5时, buf中存入的是 a b c d \0

读取 ab
SIZE = 5时, buf中是 a b \n \0 后面不管

读取 abcd SIZE = 5时 读入2次才能读完
1 -> a,b,c,d \0
2 -> \n \0


fputs用法

fputs(buf,stream);

使用fgets和fputs改写上面的mycopy函数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int main(int argc,char **argv)
{

    if(argc < 3) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
        exit(1);
    }
    FILE *fps,*fpd;
    // int ch;
    char buf[SIZE];
    fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写  
    if(fps == NULL)
    {
        fclose(fps);
        perror("fopen");
        exit(1);
    }
    fpd = fopen(argv[2],"w");
    if(fpd == NULL)
    {
        fclose(fpd);
        perror("fopen");
        exit(1);
    }
    while(fgets(buf,SIZE,fps) != NULL)
    {
        fputs(buf,fpd);
        // ch = fgetc(fps);
        // if(ch == EOF) //读到文件尾的话
        //     break;
        // fputc(ch,fpd); //把内容写到fpd中
    }

    fclose(fpd);
    fclose(fps);
    exit(0);
}

结束同样生成了复制文件

fread和fwrite

这两个适合操作数据量工整数据

当 情况1->数据量足够

当 情况2- >只有5个字节时候

------------------

fread(buf,1,10,fp);  //读取10个对象每次一个字节

情况1-> 10    -> 10字节

情况2-> 这时候读了5个字节,剩下5读不到

fread(buf,10,1,fp);  //读取1个对象每次10字节

情况1-> 1    -> 10字节

情况2 ->   数据不够,但是返回读取到的数目

这里同样用它改一个copy代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int n = 1;
int main(int argc,char **argv)
{

    if(argc < 3) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
        exit(1);
    }
    FILE *fps,*fpd;
    // int ch;
    char buf[8];
    fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写  
    if(fps == NULL)
    {
        fclose(fps);
        perror("fopen");
        exit(1);
    }
    fpd = fopen(argv[2],"w");
    if(fpd == NULL)
    {
        fclose(fpd);
        perror("fopen");
        exit(1);
    }
    while(n > 0)
    {
        n = fread(buf,1,SIZE,fps);
        fwrite(buf,1,n,fpd);
        // ch = fgetc(fps);
        // if(ch == EOF) //读到文件尾的话
        //     break;
        // fputc(ch,fpd); //把内容写到fpd中
    }

    fclose(fpd);
    fclose(fps);
    exit(0);
}

结果


还有很多IO操作的函数,详情要看man手册

print&scan...

输入man 3 printf

fseeko & ftello

fseek &ftell

定位一个流

fseek(指定流,偏移量,偏移相对位置(从当前位置向前或者向后走))

伪码

fp = fopen();
fputc(fp); 读取10次后,文件指针后移10
这个时候用 fseek往前把指针移动10
fseek(fp,-10,SEEK_CUR); //SEEK_CUR是从当前位置

再使用fgetc() 便可以读取刚才写入的内容

成功返回0,否则返回-1

ftell(指定流) 返回当前文件指针的位置 

写一个检查文件大小的demo


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define SIZE 1024
int n = 1;
int main(int argc,char **argv)
{

    if(argc < 2) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file>\n>",argv[0]);
        exit(1);
    }
    FILE *fps;
    // int ch;
    char buf[8];
    fps = fopen(argv[1],"r"); //打开文件 argv[1] 只写  
    fseek(fps,0,SEEK_END);//把文件指针移动到文件末尾
    printf("%ld\n",ftell(fps)); //打印指针位置,这里就能获得字节数目,从而得知文件大小

    fclose(fps);
    exit(0);
}

结果可以看到是606个字节

查看文件属性后确实是606个字节

fseecko ftello 比不带o的支持文件上限自定义,fseeck ftell 可以看到offset类型时long 最多支持2G文件大小

fseeck可以用于空洞文件

空洞文件: 刚建立下载文件时就占用文件大小的空间,这种文件里面全是 \0


缓冲区

fflush();

fflush() 是一个 C 标准库函数,用于刷新指定流的缓冲区。它的作用取决于参数:

  1. 如果参数是一个指向输出流的指针(stdout),则 fflush() 将强制将输出缓冲区中的内容写入到文件中。这对于确保数据被立即写入到文件中,而不是等到缓冲区满或者程序结束时才写入,非常有用。

  2. 如果参数是一个指向输入流的指针(如 stdin),fflush() 会产生未定义的行为,因为标准不允许刷新输入流的缓冲区。在输入流上调用 fflush() 通常没有意义,并且会导致不可预测的结果。

  3. 输入NULL,刷新所有打开的流

缓冲区:

用于合并系统调用,

1,行缓冲          换行时刷新,满的时候刷,强制刷新(例如fflush();)  ps  (标准输出时是这样的,因为是终端设备)

2,全缓冲        满的时候刷,强制刷新        ps(默认时基本上都是全缓冲模式,除了终端设备)

3,无缓冲          直接刷新 如stderr就是无缓冲(出错了还等什么😋)

setvbuf()   更改缓冲模式  一般用不到

setvbuf() 是一个 C 标准库函数,用于设置流的缓冲方式。它允许你手动控制输入流和输出流的缓冲区。函数的原型如下:

int setvbuf(FILE *stream, char *buffer, int mode, size_t size);

参数解释如下:

  • stream:指向 FILE 结构的指针,用于指定要设置缓冲方式的流。
  • buffer:指向一个缓冲区的指针,用于设置流的缓冲区。如果为 NULL,则系统自动分配缓冲区。
  • mode:用于指定缓冲类型的整数值,可以是以下值之一:
    • _IOFBF:完全缓冲。数据在流的内部缓冲区中被积累,直到缓冲区满或显式调用 fflush() 或 setvbuf() 时才会写入文件。
    • _IOLBF:行缓冲。数据在缓冲区中按行积累,直到出现换行符 \n 或缓冲区满时才写入文件。
    • _IONBF:无缓冲。数据直接写入文件,没有缓冲。
  • size:缓冲区大小(以字节为单位),仅在提供自定义缓冲区时使用。如果为 0,则缓冲区大小由系统决定。

getline()

用于从文件流中读取一行文本,并将其存储到一个动态分配的缓冲区中。

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
  • lineptr:一个指向字符指针的指针,用于存储读取到的文本。如果 *lineptr 为 NULL,getline() 会为其分配内存;如果不为 NULL,则 getline() 会尝试重用现有缓冲区,如果缓冲区不足以存储整行文本,则会重新分配足够大的内存。
  • n:一个指向 size_t 类型变量的指针,用于指定 *lineptr 指向的缓冲区大小。如果 *lineptr 为 NULL,则 n 的值被忽略;如果 *lineptr 不为 NULL,则 n 指定了 *lineptr 指向的缓冲区的大小。如果读取的行文本长度超过了缓冲区大小,则 getline() 会自动调整缓冲区大小。
  • stream:一个指向 FILE 结构的指针,表示要从中读取文本的流。

getline() 函数会返回读取到的字符数(包括换行符,不包含尾0),如果到达文件末尾或发生错误,则返回 -1。在成功读取一行后,*lineptr 指向的缓冲区中将包含该行文本(以 null 结尾),而 n 的值将被更新为缓冲区的大小。

打印文件每行的字符数

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
    FILE *fp;
    char *linebuf;
    size_t linesize;
    if(argc < 2) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s",argv[0]);
        exit(1);
    }

    fp = fopen(argv[1],"r");
    if(fp == NULL)
    {
        perror("fopen");
        exit(1);
    }    
    /*!!!小心段错误!!!!*/
    linebuf = NULL;
    linesize = 0;
    while(1)
    {
        if(getline(&linebuf,&linesize,fp) < 0)
        {
            break;
        }
        printf("%ld\n",strlen(linebuf));//打印字符个数
    }
    free(linebuf);//!!!!!!!!!!!!!!!!!!!!!!!!
    fclose(fp);
    
}

第一行包含换行(\n)刚好20个字符

注意用完要free(),不然就内存泄漏了😒👿👿👿👿👿

临时文件

tmpnam(char *s);  
为一个临时文件创一个名字 并发会有问题
FILE *tmpfile(void); 
创建临时文件返回文件指针(这个文件看不到的->>匿名文件)
关闭文件或者进程终止时的时候就释放了

文件IO/系统调用IO------------

文件IO操作:open,close,read,write,lseek

文件描述符实现原理

Linux——什么是文件描述符_文件描述符 linux-CSDN博客

1.系统调用IO-文件描述符实现原理_哔哩哔哩_bilibili

open close read write lseek

实现文件复制

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

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // 包含这些头文件

#define SIZE 1024
int main(int argc,char **argv)
{
    int sfd,dfd;
    int len,ret;
    char buf[SIZE];
    if(argc < 3) //检查输入参数数量是否正确
    {
        fprintf(stderr,"使用有错:%s <src_file> <dest_file\n>",argv[0]);
        exit(1);
    }

    sfd = open(argv[1],O_RDONLY); //打开文件 argv[1] 只写  
    if(sfd < 0) 
    {
        perror("open err");
        close(sfd);
        exit(1);
    }
    dfd = open(argv[2],O_WRONLY |O_CREAT|O_TRUNC,0600);
    if(dfd < 0) 
    {
        perror("open err");
        close(dfd);
         exit(1);
    }
    while(1)
    {
        len = read(sfd,buf,SIZE); //读sfd放入buf,大小为SIZE  返回字节大小
        if(len < 0)
        {
            perror("read err");
            break;
        }
        if(len == 0) break;
        ret = write(dfd,buf,len);  //写buf到dfd,长度len
        if(ret < 0) 
        {
            perror("write err");
            break;
        }
    }
    close(sfd);
    close(dfd);
    exit(0);
}

lseek 和fseek用法一样

文件IO和标准IO区别

文件IO无缓冲区,实时性强

标准IO有缓冲区,数据吞吐量大

两种IO转换

标准IO转文件IO  


文件IO转标准IO

当然,极不建议两个混着用,标准IO有缓冲区,文件指针只有刷新缓冲区的时候才移动。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // 包含这些头文件
#include <errno.h>
#include <string.h>

int main(void)
{
    putchar('a');
    write(1,"b",1);

    putchar('a');
    write(1,"b",1);

    putchar('a');
    write(1,"b",1);
    printf("\n");
    exit(0);
}

查看系统调用过程

strance ./out

系统IO先写入b b b,然后标准IO 调用系统IO一次性写入aaa\n

TIME

time ./out   查看各个部分执行时间

重定向

输出重定向到文件

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/home/qianghuizhe/c_learn2make/ttt.c"
int main(int argc,char **argv)
{
    int fd;
    close(1);
    fd = open(FNAME,O_WRONLY |O_CREAT|O_TRUNC,0666);
    if(fd < 0)
    {
        perror("open");
        exit(1);
    }
//-----------
    //puts("hello");
    printf("hello\n");
    exit(0);
}

C语言的原子操作_c语言原子操作-CSDN博客

原子操作 . 原理与底层实现___sync_val_compare_and_swap-CSDN博客

作用:解决竞争和冲突


使用dup输出重定向

dup2 可以复制文件描述符到另外一块空间,指向同一个结构体,

dup2在 fd = 1(新的fd) dup2什么都不做

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/home/qianghuizhe/c_learn2make/ttt.c"
int main(int argc,char **argv)
{
    int fd;
    fd = open(FNAME,O_WRONLY |O_CREAT|O_TRUNC,0666);
    if(fd < 0)
    {
        perror("open");
        exit(1);
    }
   // close(1);
   // dup(fd);
    dup2(fd,1);
    close(fd);
//-----------
    puts("hello");
    //printf("hello\n");
    exit(0);
}

OTHER

sysnc

fsync

fdatasync

fcntl        

ioctl  设备相关

/dev/fd/ 显示当前进程文件描述符信息

其他待补充内容

chomd 分为User、Group、及Other的权限,都是 R W X   
                                         4 2 1







代码段。。。








代码段。。。








代码段。。。








代码段。。。








  • 60
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值