【c基础】文件操作

1.fopen和fclose函数

函数原型

 FILE *fopen(const char *path, const char *mode);

参数解释:

  • 返回值:fopen打开成功,则返回有效file的有效地址,失败返回NULL。
  • path是文件路径,可以相对路径,可以绝对路径
  • mode是模式
mode说明
r或者rtr以只读的方式打开文本文件,该文件必须存在,且是可读的。在一些系统上,r 模式也可以打开二进制文件,但这并不是标准行为。与 r 模式相比,rt 模式明确地指示打开文件为文本模式,但在大多数系统上,它与 r 模式的行为相同。
w或者wt打开只写文件,若文件存在则文件长度清0,即该文件内容会消失,若文件不存在则建 立该文件只写打开或建立一个文本文件,只允许写数据。
a或者at以追加的方式打开只写文件,若文件不存在则建立该文件,若文件存在,写入的内容将 被追加到文件末尾,即文件原先的内容会被保留。(EOF符保留)
rb只读打开一个二进制文件,只允许读数据
wb只写打开或建立一个二进制文件,只允许写数据
ab追加打开一个二进制文件,并在文件末尾写数据
r+或者rt+r+以可读写的方式打开,+号表示该文件必须存在。与 r+模式相比,rt+模式明确地指示打开文件为文本模式。
w+或者wt+打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失,若文件不存在 则建立该文件。
a+或者at+以追加的方式打开可读写文件,若文件不存在则建立该文件,若文件存在,写入的内 容将被追加到文件末尾,即文件原先的内容会被保留。(原来的EOF符不保留)
rb+读写打开一个二进制文件,允许读写数据,文件必须存在
wb+读写打开或建立一个二进制文件,允许读和写
ab+读写打开一个二进制文件,允许读,或在文件末追加数据
rw+读写打开一个文本文件,允许读和写

读写文件出错的一大原因可能是没有权限。


fopen打开读取的文件内容占用的内存在堆里面,用fclose可以释放掉。

fclose关闭fopen打开的文件。只要成功用fopen打开的文件,使用完成后就一定要调用fclose关闭。fclose的参数就是fopen的返回值。

一个程序同时可以打开的文件数是有限的,如果超过系统限制,那么打开文件会失败。

一个fopen会占用一些内存,多个就会对内存消耗很大。

所以记得要fopen使用完文件后及时的fclose。


代码示例:

#include <stdio.h>

int main()
{
    // 1.判断文件是否能打开
    // FILE *p = fopen("./test.txt", "r");
    // char *filepath = "./test.txt";
    char filepath[] = "./test.txt";
    FILE *p = fopen(filepath, "w");
    if (p)
    {
        printf("%p\n", p); // 00007FF98D85FA90
        printf("open success\n");
    }
    else
    {
        printf("open fail\n");
    }

    // 2.关闭文件
    if (p)
    {
        fclose(p);
        printf("close success\n");
    }

    return 0;
}

2.文本文件与二进制文件的区别

基于字符编码vs基于值编码

计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异,简单来说,文本文件是基于字符编码的文 件,常见的编码有ASCII编码,UNICODE编码等等。二进制文件是基于值编码的文件,你可以根据具体应用,指定多少个比特代表一个值。

从上面可以看出文本文件基本上是定长编码的(也有非定长的编码如UTF-8)。而二进制文件可看成是变长编码的,因为是值编码嘛,多少个比特代表一个值,完全由你决定。

写入和读取时的区别

文本工具打开一个文件的过程是怎样的呢?拿记事本来说,它首先读取文件物理上所对应的二进 制比特流,然后按照你所选择的解码方式来解释这个流,然后将解释结果显示出来。

一般来说, 你选取的解码方式会是ASCII码形式(ASCII码的一个字符是8个比特),接下来,它8个比特8 个比特地来解释这个文件流。

例如对于这么一个文件流"01000000_01000001_01000010_01000011"(下划线''_'',为了增强可读性手动添加的), 第一个8比特''01000000''按ASCII码来解码的话,所对应的字符是字符''A'',同理其它3个8比特 可分别解码为''BCD'',即这个文件流可解释成“ABCD”,然后记事本就将这个“ABCD”显示在屏 幕上。

再比如文件流''00000000_00000000_00000000_00000001''可能在二进制文件中对应的是一 个四字节的整数int 1,在记事本里解释就变成了"NULL_NULL_NULL_SOH"这四个控制符。

字符数据本身在内存中就经过了编码,所以无论是二进制还是文本形式都是一样的,而对于非字 符数据来说,例如char i=10;如果用二进制来进行存储的话为00001010,但是如果需要用文本形式来 进行存储的话就必须进行格式化编码(对1和0分别编码,即形式为‘1’和‘0’分别对应的码值,即00000001 和 00000000写入文件中)。

  • FILE *writep = fopen("a.dat", "w"); //"w"按文本文件的字符编码写入,比如写入数字 10 (char类型), 则会将字符'1'和'0'对应的ASCII编码,即00000001 和 00000000依次写入 a.dat中
  • FILE *writep = fopen("a.dat", "wb"); //"wb"按二进制文件的二进制编码写入,比如写入 数字 10(char类型), 会将10对应的二进制编码 比如 00001010 写入 a.dat中

文本文件的存储与其读取基本上是个逆过程。而二进制文件的存取显然与文本文件的存取差不 多,只是编/解码方式不同而已。

优缺点比较

  • 一般认为,文本文件编码基于字符定长,译码容易些;二进制文件编码是变长的,所以它灵活, 存储利用率要高些,译码难一些(不同的二进制文件格式,有不同的译码方式)。
  • 文本文件每条数据通常是固定长度的。以ASCII为例,每条数据(每个字符)都是1个字节。二进制 进制文件每条数据不固定。如short占两个字节,int占四个字节,float占8个字节。
  • 文本文件只能存储char型字符变量。二进制文件可以存储char/int/short/long/float/……各种 变量值。
  • 在windows下,文本文件不一定是一ASCII来存贮的,因为ASCII码只能表示128的标识,你打开一个 txt文档,然后另存为,有个选项是编码,可以选择存贮格式,一般来说UTF-8编码格式兼容性要好一 些,而二进制用的计算机原始语言,不存贮兼容性。

3.win和linux文本文件中换行符的区别

windows读写文件时

  • windows所有的文本文件都是\r\n结尾的,而不是\n结尾的,所以这里要占2个字节。
    • 写入时,如果是文本方式写入即用"w"参数时,每遇到一个''\n'',它将其换成''\r \n'', 然后再写入文件;
    • 读取时,当文本读取即用"r"参数时,它每遇到一个''\r\n''将其反变化为''\n'',然后送到 读缓冲区。
  • 对于二进制文件,正常读写,不存在任何转换。

linux读写文件时:

  • linux所有的文本文件,本来就是\n结尾的,前面没有\r,参数b在linux是无效的
  • 二进制文件,正常读写,不存在任何转换

windows和Linux间文本文件格式转换

  • linux转windows:unix2dos -n a.txt b.txt。将a.txt 中的\n变为\r\n,然后另存为b.txt。
  • windows转linux:dos2unix -n b.txt c.txt。将b.txt 中的\r\n变为\n,然后另存为c.txt。

示例代码理解windows下"r"的存在

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
    // 1.打印每个字符和对应的ASCII码
    FILE *filep = fopen("./add.txt", "r");
    unsigned char a = 0;
    while (1)
    {
        fread(&a, 1, sizeof(char), filep);
        if (feof(filep))
        {
            break;
        }
        printf("(%c,%d)\n", a, a);
    }
    fclose(filep);
    
    // 2.获取文件大小(注意'\r'也占用了一个字节)
    struct stat st = {0};
    stat("./add.txt", &st);
    printf("size=%d\n", st.st_size); // size=12
    // 'h' 'e' 'l' 'l' 'o' '\r' '\n' 'w' 'o' 'r' 'l' 'd'
    return (0);
}
/*
(h,104)
(e,101)
(l,108)
(l,108)
(o,111)
(
,10)
(w,119)
(o,111)
(r,114)
(l,108)
(d,100)
size=12
*/

4.getc和putc函数

getc

已字节为单位读写文件。

函数原型

char getc(FILE *stream);

说明:

  • getc的参数是fopen成功打开文件后返回的指针,getc的返回值是一个char
  • getc的功能是以字节为单位读取文件内容。注意当文本文件中存在汉字内容时,读取会出现乱码,因为一个汉字占2个字节(gbk编码时)或者3个字节(utf-8编码时)。
  • 文本文件的最后结束标示是-1,是一个宏常量EOF
#define EOF -1

示例代码

#include <stdio.h>

int main()
{
    // 1.判断文件是否能打开
    char filepath[] = "./test.txt";
    FILE *p = fopen(filepath, "r");
    if (p)
    {
        printf("open %s success\n", filepath);
        // 2.读取内容
        char c = getc(p); // 读取一个字节内容
        printf("%c\n", c);
        c = getc(p); // 再读取一个字节内容
        printf("%c\n", c); 
    }
    else
    {
        printf("open %s fail\n", filepath);
    }

    // 3.关闭文件
    if (p)
    {
        fclose(p);
        printf("close success\n");
    }
    return 0;
}

用循环打印文件所有内容

#define EOF -1

EOF不是文件的一部分内容,只是文件结束的标识

#include <stdio.h>

int print_file(char *filepath)
{
    char c;
    FILE *p = fopen(filepath, "r");
    if (p)
    {
        printf("open %s success\n", filepath);
        c = getc(p);
        while (c != EOF)
        {
            printf("%c", c);
            c = getc(p);
        }

        fclose(p);
    }
    else
    {
        printf("open %s fail\n", filepath);
        return -1;
    }

    return 0;
}

int main()
{
    // 打开文件
    //char filepath[] = "./test.txt";
    //char filepath[] = "./test.c";
    char filepath[] = "./test.txt";
    int ret = print_file(filepath);
    printf("ret=%d\n", ret);

    return 0;
}

自定义读取文件一行内容的函数

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

int read_line(char *filepath, char **file_data)
{
    FILE *filep = fopen(filepath, "r");
    if (!filep)
    {
        perror("fopen");
        return -1;
    }

    int line_size = 1;
    char *content = (char *)malloc(line_size);
    if (content == NULL)
    {
        perror("malloc");
        fclose(filep);
        return -2;
    }

    // 初始化content为空字符串 
    content[0] = '\0';

    int count = 0;
    int is_finish = 0;
    int i = 0;
    while (1)
    {
        char temp[1024] = {0};
        for (i = 0; i < 1023; i++)
        {
            char c = getc(filep);
            if (c == '\n' || c == EOF)
            {
                is_finish = 1;
                break;
            }
            temp[i] = c;
            count++;
        }

        
        if (count > 0) // 只有在读取到字符时才进行内存分配和字符串拼接
        {
            if (count >= line_size)
            {
                char *ret = (char *)realloc(content, count+1);
                if (ret == NULL)
                {
                    perror("realloc");
                    free(content); // 释放之前的内存
                    fclose(filep);
                    return -3;
                }
                content = ret;
                line_size = count + 1;
            }

            strcat(content, temp);

            // 添加空字符,确保字符串正确终止
            content[count] = '\0'; 
        }

        if (is_finish)
        {
            break;
        }
       
       
    }
    
    // printf("(%s)\n", content);   // 文件内容
    // printf("count=%d\n", count); // 字节数

    *file_data = content;

    fclose(filep);

    return 0;
}

int main()
{
    char filepath[100] = "./test.txt";
    char *file_content;

    int ret = read_line(filepath, &file_content);
    printf("ret = %d\n", ret);

    if (ret == 0)
    {
        printf("(%s)\n", file_content);
        printf("content len=%d\n", strlen(file_content));

        free(file_content);  
    }
    else
    {
        printf("Error reading line from file\n");  
    }


    return 0;
}

putc

函数原型

int putc(int c, FILE *stream);

第一个参数是要写入的char, 第二个参数是fopen返回的文件指针

getc参数的文件指针必须是用r模式打开,putc必须是用w模式打开

#include <stdio.h>

int main()
{
    // 打开文件
    char filepath[] = "./test.txt";
    FILE *filep = fopen(filepath, "w");
    if (filep)
    {
        // 写入内容
        putc('a', filep);
        putc('\n', filep);
        putc('b', filep);
        putc('c', filep);
        // 关闭文件
        fclose(filep);
    }
    else
    {
        printf("open fail\n");
    }
    
    return 0;
}

自定义将字符串写入文件的函数

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

void write_str(char *filepath, const char *s, unsigned int len)
{
    FILE *filep = fopen(filepath, "w");
    if (filep)
    {
        for (int i = 0; i < len; i++)
        {
            printf("%c", s[i]);
            putc(s[i], filep);
        }
        fclose(filep);
    }
    else
    {
        printf("open fail\n");
    }
}

int main()
{
    char filepath[] = "./test.txt";
    char mystr[50] = "hello world";
    int len;
    // len = sizeof(mystr)/sizeof(mystr[0]);
    len = strlen(mystr); //用字符串长度
    write_str(filepath, mystr, len);

    return 0;
}

自定义文件拷贝函数实现命令行命令

使用c语言的main函数还可以自定义命令行的命令

比如可执行文件的名字叫 copyfile,通过设计源代码的main函数可以达到命令行的作用

  1. gcc test.c -o copyfile
  2. copyfile test.txt test_write.txt
  3. 可以实现将test.txt拷贝为test_write.txt

示例代码

#include <stdio.h>

void copyfile(char *srcPath, char *tarPath)
{
    FILE *readp = fopen(srcPath, "r");
    FILE *writep = fopen(tarPath, "w");
    if (readp && writep)
    {
        while (1)
        {
            char c = getc(readp);
            if (c == EOF)
            {
                break;
            }
            putc(c, writep);
        }
    }

    fclose(readp);
    fclose(writep);
}

int test()
{
    char filepath[] = "./test.txt";
    char writePath[] = "./test_write.txt";
    copyfile(filepath, writePath);
    return 0;
}

int main(int argc, char **args)
{
    if (argc < 3)
    {
        printf("params less than 3");
        return -1;
    }

    copyfile(args[1], args[2]);

    return 0;
}


运行

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main test.txt test_copy.txt

简单的文件加密和解密

加密:将一份文件拷贝时,给每个字符通过加减乘除或者指定函数等指定规则变为另一个字符再写入另一个文件。

解密:按加密的方式逆向获得原字符。

示例代码:

#include <stdio.h>

void copyfile(char *srcPath, char *tarPath, char isEncode)
{
    FILE *readp = fopen(srcPath, "r");
    FILE *writep = fopen(tarPath, "w");
    if (readp && writep)
    {
        while (1)
        {
            char c = getc(readp);
            if (c == EOF)
            {
                break;
            }

            if (isEncode == '+')
                c++;
            else
                c--;
            
            putc(c, writep);
        }
    }

    fclose(readp);
    fclose(writep);
}

int test()
{
    char filepath[] = "./test.txt";
    char writePath[] = "./test_write.txt";
    copyfile(filepath, writePath, '+');
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc < 4)
    {
        printf("params less than 3");
        return -1;
    }

    copyfile(argv[1], argv[2], argv[3][0]);

    return 0;
}

运行效果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main test.txt test_encode.txt +
[root@localhost test]# cat test.txt
hello world
[root@localhost test]# cat test_encode.txt 
ifmmp!xpsme
           [root@localhost test]# 
[root@localhost test]# ./main test_encode.txt test_decode.txt -
[root@localhost test]# cat test_decode.txt 
hello world
[root@localhost test]# 

5.EOF与feof函数

程序怎么才能知道已经到结尾了呢?文本文件内容的最后一个char是EOF。

EOF不属于文件的内容,只是文件的结尾标识,而且也不要直接用-1来替代EOF。

只有文本文件才可以通过EOF来判断文件的结尾标示,文本文件的结尾标示是EOF,但二进制文件不是,对于二进制文件EOF是无效的,二进制文件就需要用feof这个函数。feof也可以用在文本文件。

函数原型:

int feof(FILE *stream);

参数就是fopen函数返回的指针。

文件指针所在位置:初始在第一行的开头,即第1个字符前面那。后续每次读取了内容,就在已读取内容的最后一个字符和下一个字符之间。读取内容的最后一个字符是当前行的换行符时,读取后,文件指针在下一行的开头。

feof的原理:EOF在最后一行末尾,在最后一行时,feof(filep)不会为True,读文件的函数不仅会把最后一行的文件内容读到,也会把EOF读到。示例代码理解:

#include <stdio.h>

// 1.用feof
int main01()
{
    char filepath[] = "./test.txt";
    FILE *p = fopen(filepath, "r");

    int count = 0;

    if (p)
    {
        while (!feof(p))
        {
            char c = getc(p);
            // if (c == EOF){break;} // 不读取最后一个字符,即文件结尾符EOF

            // 打印字符和对应的ASCII码
            printf("(%c,%d)=>", c, c);
            count++;
        }
        fclose(p);
    }
    else
    {
        printf("open fail\n");
    }

    printf("count = %d\n", count); // count = 8

    return 0;
}



// 2.用EOF
int main02()
{
    char filepath[] = "./test.txt";
    FILE *p = fopen(filepath, "r");

    int count = 0;

    char c = getc(p);
    if (p)
    {
        while (c != EOF)
        {
            // 打印字符和对应的ASCII码
            printf("(%c,%d)=>", c, c);
            count++;
            c = getc(p);
        }
        fclose(p);
    }
    else
    {
        printf("open fail\n");
    }

    printf("count = %d\n", count); // count = 7
    return 0;
}


int main()
{
    main01();
    main02();

    return 0;
}

运行结果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# cat test.txt
hello world
[root@localhost test]# ./main
(h,104)=>(e,101)=>(l,108)=>(l,108)=>(o,111)=>( ,32)=>(w,119)=>(o,111)=>(r,114)=>(l,108)=>(d,100)=>(
,10)=>(�,-1)=>count = 13
(h,104)=>(e,101)=>(l,108)=>(l,108)=>(o,111)=>( ,32)=>(w,119)=>(o,111)=>(r,114)=>(l,108)=>(d,100)=>(
,10)=>count = 12
[root@localhost test]# 

6.fputs和fgets 按照行读写文件

fputs 将字符串写入文件

函数原型

int fputs(const char *str, FILE *stream);

该函数返回一个非负值,如果发生错误则返回 EOF(-1)。

示例代码:

#include <stdio.h>

int main()
{
    FILE *filep = fopen("./fputs.txt", "w");
    if (filep)
    {
        fputs("hello world\n", filep);
        fputs("xxxx\n", filep);

        char info[10] = {0};
        sprintf(info, "work %d%d%d\n", 9, 9, 6);
        fputs(info, filep);

        fclose(filep);
    }
    else
    {
        perror("open file fail");
    }
        
    return 0;
}

fgets 从键盘输入或者文件输入

函数原型

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

参数:

  • s: 字符型指针,指向存储读入数据的缓冲区的地址
  • size: 从流中读入 size - 1 个字符,放入s的前size-1个位置,然后将第size个位置处放'\0' 字符。
  • stream: 指向读取的流。如果要从键盘输入,则传入stdin即可。如果要将文件作为输入来源,则传入对应的文件指针即可。

返回值:

  • 如果读入错误或遇到文件结尾(EOF),则返回NULL
  • fgets的返回值是 char *,代表函数读到的字符串的首地址,如果fgets到了文件末尾,继续调 用,返回NULL

fgets读取原理:

  • fgets 执行时会从文件流的当前位置读取 size - 1 个字符,然后在读取到的这些字符后面添加 一个 '\0' 字符并将这些字符放入 s 中,也就是第size位置处放'\0' 字符,作为字符串的结尾。
  • 如果 fgets 在执行时发现文件流的当前位置到换行符或文件末尾之间的这些字符不够 size - 1 个字符,则将不够数的字符统统用 '\0' 代替。
  • 换行符也是当前行的内容
#include <stdio.h>
#include <string.h>

int main1()
{
    FILE *filep = fopen("./fputs.txt", "w");
    if (filep)
    {
        while (1)
        {
            // get data from keyboard
            char buf[1024] = {0};
            fgets(buf, sizeof(buf), stdin);
            if (strncmp(buf, "exit", 4) == 0)
                break;
            
            // write data into file
            fputs(buf, filep);
        }

        fclose(filep);
    }
    
    return 0;
}

int main2()
{
    FILE *filep = fopen("./fputs.txt", "r");
    char buff[10] = {0};
    if (filep)
    {
        // read data line by line from file
        while (fgets(buff, sizeof(buff), filep) != NULL)
        {
            printf("(%s)", buff);
        }
        fclose(filep);
    }

    return 0;
}

int main()
{
    main1();

    main2();

    return 0;
}

运行效果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main
hello world
work for life
exit
(hello wor)(ld
)(work for )(life
)[root@localhost test]# 

用fgets和fputs进行文件拷贝

#include <stdio.h>

void copyfile(char *srcPath, char *tarPath)
{
    FILE *readp = fopen(srcPath, "r");
    if (!readp)
    {
        printf("fopen %s fail\n", srcPath);
        return;
    }

    FILE *writep = fopen(tarPath, "w");
    if (readp && writep)
    {
        while (!feof(readp))
        {
            char buff[1024] = {0};
            if (fgets(buff, sizeof(buff), readp) == NULL)
            {
                break;
            }
            // printf("(%s)\n", buff);
            fputs(buff, writep);
        }

        fclose(readp);
        fclose(writep);
    }

}

int main()
{
    char filepath[] = "./test.txt";
    char writePath[] = "./test_write.txt";
    copyfile(filepath, writePath);

    return 0;
}

7.fprintf和fscanf 按指定格式读写文件

fscanf从文件内容中读取指定的内容

fprintf输出指定格式内容到文件中

int fscanf(FILE *stream, const char *format, [argument...])
int fprintf(FILE *stream, const char *format, [ argument ]...)

fscanf函数返回值:

  • 读文件成功,则返回成功读取的项数
  • 读文件失败,则返回EOF

fprintf()函数返回值

  • 写文件成功,则返回写入的总字符数
  • 写文件失败,则返回负值

解析文件内容并追加结果

cal.txt

12+3=
14*6=
5/7=
32-56=

示例代码

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

int myfunc(int a, char b, int c)
{
    switch (b)
    {
    case '+':
        return a + c;
    case '-':
        return a - c;
    case '*':
        return a * c;
    case '/':
        if (c > 0)
            return a / c;
        else
            return 0;
    default:
        printf("error");
        return 0;
    }
}

int main()
{
    FILE *filep = fopen("./cal.txt", "r");
    FILE *writep = fopen("./cal_f.txt", "w");
    if (filep && writep)
    {
        while (1)
        {
            int a, c;
            char b;
            int ret;
            //读取文件一行
            ret = fscanf(filep, "%d%c%d=", &a, &b, &c);
            printf("\nret=%d\n", ret);
            /*
            当前文件最后一行具有内容时,此时EOF在最后一行末尾
            文件指针在最后一行时,feof(filep)会为真,fgets调用buff也会读取到内容,
            在fgets调用后feof(filep)会为假
            因此判断feof(filep)和处理内容谁前谁后很关键
            先处理内容,再判断feof(filep)是否退出
            */

            // 处理内容
            if (ret == 3)
            {
                printf("%d%c%d=%d\n", a, b, c, myfunc(a, b, c));
                fprintf(writep, "%d%c%d=%d\n", a, b, c, myfunc(a, b, c));
            }

            // 判断文件是否结束
            if (feof(filep))
            {
                break;
            }

        }

        fclose(filep);
        fclose(writep);
    }

    return 0;
}

cal_f.txt

12+3=15
14*6=84
5/7=0
32-56=-24

文件内容特定条件查找

文件:name_age.txt

name=liudehua,age=50
name=anbei,age=30
name=zhangxueyou,age=45
name=canglaoshi,age=70

要求:打印年龄第二大的人的姓名

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

#define NUM 100

int main()
{
    FILE *filep = fopen("./name_age.txt", "r");

    int maxage = 0;
    int max2age = -1;
    char maxname[100] = {0};
    char max2name[100] = {0};

    if (filep)
    {
        while (1)
        {
            printf("------------------\n");
            //读取文件一行
            char buff[1024] = {0};
            fgets(buff, sizeof(buff), filep);
            /*
            当前文件最后一行具有内容时,此时EOF在最后一行末尾
            文件指针在最后一行时,feof(filep)会为真,fgets调用buff也会读取到内容,
            在fgets调用后feof(filep)会为假
            因此判断feof(filep)和处理内容谁前谁后很关键
            先处理内容,再判断feof(filep)是否退出
            */
            printf("(%s)\n", buff);

            if (strncmp(buff, "name", 4) == 0)
            {
                //用,分割得到姓名
                char *s;
                s = strtok(buff, ",");
                printf("%s\n", s);
                char name[100] = {0};
                sscanf(s, "name=%s", name);
                printf("%s\n", name);

                //用,分割得到年龄
                s = strtok(NULL, ",");
                printf("%s", s);
                int age = 0;
                sscanf(s, "age=%d", &age);
                printf("%d\n", age);

                //更新最大值和姓名
                if (age > maxage)
                {
                    strcpy(max2name, maxname);
                    strcpy(maxname, name);
                    max2age = maxage;
                    maxage = age;
                }
                else if ((max2age < age) && (age < maxage))
                {
                    strcpy(max2name, name);
                    max2age = age;
                }
            }

            if (feof(filep))
            {
                break;
            }
            
        }
        fclose(filep);
    }

    printf("------------------\n");
    printf("%s,%d\n", maxname, maxage);
    printf("%s,%d\n", max2name, max2age);

    return 0;
}

8.stat函数

头文件

#include <sys/stat.h>
#include <sys/types.h>

函数原型

int stat(const char * Filename, struct stat *_Stat)

参数:

  • 第一个参数代表文件名
  • 第二个参数是struct stat结构,传出参数
  • 返回值:执行成功返回0,失败返回-1

函数作用:通过传出参数,得到文件的属性,包括文件建立时间,文件大小,最后访问时间,最后修改时间等信息,比如stat.st_size;得到文件大小,单位字节

结构体struct stat的参数说明

struct stat
{
    dev_t st_dev; //device 文件的设备编号
    ino_t st_ino; //inode 文件的索引节点
    mode_t st_mode; //protection 文件的类型和存取的权限
    nlink_t st_nlink; //number of hard links 连到该文件的硬连接数目,
    //刚建立的文件值为1.
    uid_t st_uid; //user ID of owner 文件所有者的用户识别码
    gid_t st_gid; //group ID of owner 文件所有者的组识别码
    dev_t st_rdev; //device type 若此文件为装置设备文件, 则为其设备编号
    off_t st_size; //total size, in bytes 文件大小, 以字节计算
    unsigned long st_blksize;
    //blocksize for filesystem I/O 文件系统的I/O 缓冲区大小.
    unsigned long st_blocks;
    //number of blocks allocated 占用文件区块的个数,
    //每一区块大小为512 个字节.
    time_t st_atime; //time of lastaccess 文件最近一次被存取或被执行的时间,
    //一般只有在用mknod、utime、read、write 与tructate 时改变.
    time_t st_mtime; //time of last modification 文件最后一次被修改的时间,
    // 一般只有在用mknod、utime 和write 时才会改变
    time_t st_ctime; //time of last change i-node 最近一次被更改的时间,
    //此参数会在文件所有者、组、权限被更改时更新
};

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
//#include <unistd.h>
#include <time.h>
#define NUM 100

int main()
{
    struct stat st = {0};
    int ret = stat("test.txt", &st);
    //调用完stat函数后,就得到了文件的各种属性

    printf("%d\n", ret); // 0
    if (ret == 0)
    {
        //文件大小
        int size = st.st_size;
        printf("%d\n", size); // 28

        struct tm *info;
        char buffer[80];

        //最近一次被执行的时间
        time_t atime = st.st_atime;
        printf("%d\n", atime); // 1668269249
        info = localtime(&atime);
        strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
        printf("%s\n", buffer); // 2022-11-13 00:07:29

        //最后一次被修改的时间
        time_t mtime = st.st_mtime;
        printf("%d\n", mtime); // 1668261399
        info = localtime(&mtime);
        strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
        printf("%s\n", buffer); // 2022-11-12 21:56:39
        
        //最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新
        time_t ctime = st.st_ctime;
        printf("%d\n", ctime); // 1668252245
        info = localtime(&mtime);
        strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
        printf("%s\n", buffer); // 2022-11-12 21:56:39
    }
    else
    {
        printf("stat error\n");
    }

    return 0;
}

9.fread和fwrite 读写二进制数据

函数说明

fgets fputs fprintf, fscanf这些函数都是针对文本文件读写的,不能对一个二进制文件进行读写。

除了文本文件之外的文件都是二进制,比如图像,可执行程序,音乐,视频这些都是二进制 的(其实文本文件是特殊的二进制文件,只是编码方式不同而已,对于数值类型,文本文件 按字符编码,二进制文件按值编码)

fprintf等都是往文本文件里面写一个字符串,比如将123456写入文件,实际上把‘1’, ‘2’, '3', '4', '5', '6'这6个字符写入文件。如果要把一个int整数写入文件,这个文件就不是文本文件了。

函数原型

size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
size_t fwrite(void *buffer, size_t size, size_t count, FILE *stream);

参数:

  • 第一个参数是buff的内存地址,表示要写什么
  • 第二个参数是每个单位的大小
  • 第三个参数是写 多少个单位(只需要第二个参数和第三个参数的乘积与 第一个参数指定的内存区域一样大即可)
  • 第四个参数是fopen返回的文件指针。

这个函数以二进制形式对文件进行操作,不局限于文本文件

返回值:返回实际写入的数据块数目

示例代码

#include <stdio.h>

int main1()
{
    FILE *filep = fopen("./test.dat", "w");

    int i = 100;
    fwrite(&i, 1, sizeof(int), filep);
    
    short x = 50;
    fwrite(&x, 1, sizeof(short), filep);

    fclose(filep);

    return (0);
}

int main2()
{
    FILE *filep = fopen("./test.dat", "r");

    int a = 0;
    fread(&a, 1, sizeof(int), filep);
    printf("a=(%d)\n", a);

    short b = 0;
    fread(&b, 1, sizeof(short), filep);
    printf("b=(%d)\n", b);

    
    fclose(filep);
    return (0);
}


int main()
{
    // 1.fwrite
    main1();

    // 2.fread
    main2();

    return 0;
}

fread的返回值

  • fread第二个参数代表了一个单位多大,第三个参数一次要读多少单位
  • fread返回值是成功读取到的单位个数。fread的返回值是,实际读到的内容大小/单位大 小,比如函数读的单位是2个字节,实际读了8个字节的内容,则fread返回4。再比如单位是 4个字节,但只读了2个字节的内容,则返回0
  • 当fread的第二个参数是1时,可以认为fread返回的是读取到的字节数
#include <stdio.h>
int main()
{
    FILE *filep = fopen("./test.dat", "r");
    int a = 0;
    while (1)
    {
        int res = fread(&a, 1, sizeof(int), filep);
        printf("%d\n", res); //4
        if (feof(filep))
        {
            break;
        }
        printf("%d\n", a);
    }
    fclose(filep);
    return (0);
}

实现二进制文件的拷贝

比如图像,可执行程序,音乐,视频这些都是二进制的,都可以用程序拷贝

一个字节一个字节的拷贝

下面代码速度较慢,因为是读一个字节,写一个字节

#include <stdio.h>

int main()
{
    FILE *readp = fopen("./test.txt", "rb");
    if (readp)
    {
        printf("open read");
    }

    FILE *writep = fopen("./test2.txt", "wb");
    if (writep)
    {
        printf("open write");
    }

    while (1)
    {
        char a = 0;
        fread(&a, 1, 1, readp);
        if (feof(readp))
        {
            break;
        }
        fwrite(&a, 1, 1, writep);
    }
    
    fclose(readp);
    fclose(writep);

    return 0;
}

用栈每次拷贝多个字节

读1024个字节,写1024个字节,不够1024时,读多少写多少

#include <stdio.h>

int main()
{
    FILE *readp = fopen("./test.txt", "rb");
    FILE *writep = fopen("./test2.txt", "wb");
    if (writep && readp)
    {
        while (!(feof(writep)))
        {
            char a[1024] = {0};
            int res = fread(&a, 1, sizeof(a), readp);
            fwrite(&a, 1, res, writep); //用res读多少,写多少
        }
    }
    fclose(readp);
    fclose(writep);
    return 0;
}

用堆动态分配内存拷贝

可以根据文件大小,动态分配一个堆内存出来

但是如果这个文件的内存很大一下把内存撑爆了

可以得到文件size后,判断size大小决定是否分成几次来写

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#define NUM 1024 * 64 //64 k

int main()
{
    FILE *readp = fopen("./test.txt", "rb");
    FILE *writep = fopen("./test3.txt", "wb");
    if (writep && readp)
    {
        struct stat st = {0};
        stat("test.txt", &st);
        int size = st.st_size;
        if (size >= NUM)
        {
            size = NUM;
        }

        char *buff = malloc(size);

        while (!(feof(readp)))
        {
            //char buff[1024] = {0};
            int res = fread(buff, 1, size, readp);
            fwrite(buff, 1, res, writep);
        }

        free(buff);
        fclose(readp);
        fclose(writep);
        printf("success");
    }
    
    return 0;
}

读写结构体数据

将结构体数据写入文件中,读取时直接就拿到这个结构体,方便。

把一个结构变量写入文件的时候,结构变量成员的对齐内存浪费部分同样也会写入文件。

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

struct man
{
    char name[20];
    int age;
};

int main()
{
    //将结构体等数据写入dat文件
    char filename[] = "xxx.dat";
    FILE *writep = fopen(filename, "wb");

    struct man m = {"tom", 12};
    fwrite(&m, 1, sizeof(struct man), writep);

    int x = 100;
    fwrite(&x, 1, sizeof(x), writep);

    fclose(writep);

    //从dat中读取数据
    FILE *readp = fopen(filename, "rb");

    struct man newm;
    fread(&newm, 1, sizeof(newm), readp);
    printf("%s, %d\n", newm.name, newm.age); // tom, 12

    int j = 0;
    fread(&j, 1, sizeof(int), readp); // j = 100
    printf("j = %d\n", j);

    fclose(readp);

    //读到堆中
    readp = fopen(filename, "rb");
    struct man *datap = malloc(sizeof(struct man));
    fread(datap, 1, sizeof(struct man), readp);
    printf("%s, %d\n", datap->name, datap->age); // tom, 12

    free(datap);

    int *intp = malloc(sizeof(int));
    fread(intp, 1, sizeof(intp), readp);
    printf("%d\n", *intp); // 100
    
    free(intp);
    fclose(readp);

    return 0;
}

10.fseek函数

FILE指针原理

  • FILE结构内部是有一个指针的,每次调用文件读写函数,这些函数就会自动移动这个指针 默认情况下指针只能从前往后移动。
  • c语言文件读写库函数都是自动维护FILE里面的相关成员,包括文件读写当前位置。 每次调用fgets,或者fread,下次再调用的时候,就会自动从已读取内容后面开始。
  • feof为什么能知道是否到了文件结尾了呢?fread或者fgets这些函数如果读完文件所有内容,他们会设置FILE里面相关变量的值。
  • feof只是判断FILE结构里面相关变量的值是否为文件已经结尾状态。
  • fopen成功打开一个文件后,文件指针默认都是在0 SEEK_SET这个位置。

fseek函数使用与技巧

事实上这个指针位置我们可以自己来调,需要用fseek这个函数。

int feek(FILE *_File, long _Offset, int _Origin);
  • 第一个参数是fopen返回的文件指针
  • 第二个参数是偏移量。正数表示向后偏移,负数表示向前偏移。
  • 第三个参数是设置从哪里开始偏 移,可能取值为:SEEK_CUR,SEEK_END或者SEEK_SET。
SEEK_SET: 文件开头
SEEK_CUR:文件当前位置
SEEK_END:文件结尾

函数作用:设置文件的指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset个字节的位置,函数返回0。如果执行失败则不改变stream指向的位置,函数返 回一个非0值。

一些小技巧

  • fseek(readp, 0, SEEK_SET); 回到文件开始位置
  • fseek(readp, 0, SEEK_END); 回到文件结尾位置

注意事项

  • 实验得出,超出文件末尾位置,还是返回0,往回偏移超出首位置,还是返回0,请小心使 用。
  • 并且超出文件末尾或者首位置,读到的数据都是0

示例代码理解

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

int main()
{
    FILE *writep = fopen("a.dat", "w");
    char a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    fwrite(a, 1, sizeof(a), writep);
    fclose(writep);

    FILE *readp = fopen("a.dat", "r");
    fseek(readp, 2, SEEK_SET);
    // while (!feof(readp))
    for (int i = 0; i < 2; i++)
    {
        char a[2];
        fread(a, 1, sizeof(a), readp);
        printf("%d, %d\n", a[0], a[1]);
    }
    fclose(readp);

    /*
    3, 4
    5, 6
    */

    return 0;
}

用fseek快速生成超大文件

快速生成一个超大文件,因为需要经常测试代码的文件读写情况。

原始的慢方法

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

int main()
{
    FILE *writep = fopen("a.dat", "w");
    for (int i = 0; i < 10000000; i++)
    {
        char a = 0;
        fwrite(&a, 1, sizeof(a), writep);
    }
    fclose(writep);
    return 0;
}

用fseek技巧快速生成(超大空文件)

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

int main()
{
    FILE *writep = fopen("a.dat", "w");
    fseek(writep, 1000000, SEEK_SET);
    char a = 0;
    fwrite(&a, 1, sizeof(a), writep);
    fclose(writep);
    return 0;
}

11.ftell函数

函数ftell用于得到文件指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁地前后移动,程序不容易确定文件文件的当前位置。

long loc = ftell(fp);

以下代码通过ftell函数和feof函数,可以得出:

  • 文件的EOF在最后一行末尾,无论最后一行是"文件内容+EOF",还是只有"EOF", feof(readp)都为假
  • fread读最后一块内容时:
    • 如果是"文件内容+EOF",则会去掉EOF
    • 如果是只有"EOF",则读不到任何数据,此时用fread函数不会修改传入地址的值

示例代码

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

int main()
{
    FILE *writep = fopen("a.dat", "w");
    char a[3] = {1, 2, 3};
    fwrite(a, 1, sizeof(a), writep);
    fclose(writep);

    printf("--------------\n");
    FILE *readp = fopen("a.dat", "r");
    //fseek(readp, 0, SEEK_SET);
    long loc = ftell(readp);
    printf("loc = %lu\n", loc); //loc = 0

    printf("--------------\n");
    char b;
    while (!feof(readp))
    {
        fread(&b, 1, 1, readp);
        printf("%d\n",b);
        long loc = ftell(readp);
        printf("loc = %lu\n", loc);
    }

    printf("--------------\n");
    fseek(readp, 0, SEEK_END);
    loc = ftell(readp);
    printf("loc = %lu\n", loc);
    char x = 0;
    fread(&x, 1, 1, readp);
    printf("x = %d\n",x);

    printf("--------------\n");
    fseek(readp, -1, SEEK_END);
    loc = ftell(readp);
    printf("loc = %lu\n", loc);
    char y = 0;
    fread(&y, 1, 1, readp);
    printf("y = %d\n", y);

    fclose(readp);

    return 0;
}

运行结果

--------------
loc = 0
--------------
1
loc = 1
2
loc = 2
3
loc = 3
3
loc = 3
--------------
loc = 3
x = 0
--------------
loc = 2
y = 3

12.fflush函数和文件读写缓冲区

c语言所有的文件操作函数都是缓冲区函数

以下代码只有在fclose(writep);后文件a.txt中才会有内容,说明是缓冲区库函数

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

int main()
{
    FILE *writep = fopen("a.txt", "w");
    while (1)
    {
        char a[100] = {0};
        scanf("%s", a);
        if (strcmp(a, "exit") == 0)
        {
            break;
        }
        fprintf(writep, "%s\n", a);
    }
    fclose(writep);
    return 0;
}

用ffush函数跳过内存直接写入磁盘

int fflush(FILE *_File)

由于fflush是实时的将缓冲区的内容写入磁盘,所以不要大量去使用,因为fflush相当于是直接 用磁盘读写而不是通过内存再到磁盘,所以速度慢,而且频繁读写磁盘会降低磁盘寿命,但如果 是重要的数据,可以用fflush写入磁盘。

13.remove 删除文件

删除指定文件

int remove(const char *Filename);

没有该文件时不会报错

#include <stdio.h>

int main()
{
    char filename[] = "xxx.txt";
    FILE *writep = fopen(filename, "w");
    fclose(writep);
    remove(filename);
    return 0;
}

14.rename 文件改名或者剪切

int rename(const char *OldFilename, const char *NewFilename);

示例代码

#include <stdio.h>
int main()
{
    char filename[] = "xxx.txt";
    FILE *writep = fopen(filename, "w");
    fclose(writep);
    char newname[] = "../yyy.txt";
    rename(filename, newname);
    return 0;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/560367.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

“手撕“三大特性之一的<继承>(上)

目录 一、为什么需要继承 二、什么是继承 三、继承怎么写 四、成员的访问 1.父类与子类的成员变量不同名 2.父类与子类的成员变量同名 3.父类与子类的成员方法不同名 4.父类与子类的成员方法同名 五、super关键字 一、为什么需要继承 先让我们看一段Java代码&#…

Unity地形关联出错的解决办法以及地形深度拷贝

问题 最近发现unity地形系统的一个bug&#xff0c;导入的场景地形数据关联错乱了&#xff0c;关联到别的场景的地形数据了&#xff0c;meta替换了也没用&#xff0c;不清楚它具体是怎么关联的。 看下面的案例&#xff1a; 可以看到正常这个场景的地形数据应该关联的是Scene_E…

力扣HOT100 - 142. 环形链表 II

解题思路&#xff1a; public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();while (head ! null) {if (!set.add(head)) {return head;}head head.next;}return null;} }

文心一言 VS 讯飞星火 VS chatgpt (241)-- 算法导论17.3 7题

七、为动态整数多重集 S (允许包含重复值)设计一种数据结构&#xff0c;支持如下两个操作&#xff1a;① INSERT(S,x) 将 x 插入 S 中&#xff1b;② DELETE-LARGER-HALF(S) 将最大的 ⌈|S|/2⌉ 个元素从S中删除。解释如何实现这种数据结构&#xff0c;使得任意 m 个 INSERT 和…

Linux的firewalld防火墙

介绍firewalld&#xff1a; ①、firewalld&#xff08;Dynamic Firewall Manager of Linux systems&#xff0c;Linux系统的动态防火墙管理器&#xff09;服务是默认的防火墙配置管理工具&#xff0c;它拥有基于CLI&#xff08;命令行界面&#xff09;和基于GUI&#xff08;图…

Web Tours系统使用说明书

1.系统简介 产品名称&#xff1a;LoadRunner的Web Tours系统 产品功能&#xff1a;注册和登录用户信息、订票办理、退片办理、查询已定票信息、退出系统 系统默认用户&#xff1a;用户名jojo 密码&#xff1a;bean 2. 功能简介 2.1 注册用户信息 点击 sign up now&#xff…

(自学用)正演理论

基于波动方程如何解决数值频散问题——快速正演方法 NAD方法&#xff1a; 怎样离散/逼近高阶偏导数&#xff08;如何采样&#xff09;&#xff1a; 传统方法是用某一点及其周围点的函数f的线性组合来逼近导数。只有函数值&#xff0c;要想提高精度&#xff0c;压制数值频散就必…

(C语言)fscanf与fprintf函数详解

目录 1 fprintf详解 1.1 向文件流中输入数据 1.2 向标准输入流写数据 2. fscanf函数详解 2.1 从文件中读取格式化数据 2.2 从标准输入流中读取格式化数据 1 fprintf详解 头文件&#xff1a;stdio.h 该函数和printf的参数特别像&#xff0c;只是多了一个参数stream&#…

Unity类银河恶魔城学习记录13-5,6 p146 Delete save file,p147 Encryption of saved data源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili FileDataHandler.cs using System; using System.IO; using UnityEngine; p…

现代农业AI智能化升级之路:机器学习在现代农业领域的现状与未来发展

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

Android AIDL接口

一.AlDI接口简介 AIDL&#xff08;Android Interface Definition Language&#xff09;是一种 IDL 语言&#xff0c;用于生成可以在 Android 设备上两个进程之间进行进程间通信&#xff08;IPC&#xff09;的代码。 通过 AIDL&#xff0c;可以在一个进程中获取另一个进程的数据…

力扣112,路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 是指没有子节点…

.cur 鼠标光标编辑器

详解透明贴图和三元光栅操作 - CodeBus 鼠标指针文件格式解析——Windows&#xff08;二&#xff09; (qq.com) [C/C] RGBA数组生成Windows下的ico文件_c ico格式-CSDN博客 色环设计 - CodeBus 左键绘制 右键选颜色 ctrl右键设置鼠标热点 F1导出.cur文件 //代码来源&…

分组查询得到一列多行数据后,怎么用来和表中的某列数据进行一一比较

#10&#xff09;查询每个学生超过他自己选修课程平均成绩的课程号 这里要先进行分组得到每个学号对应的平均成绩&#xff0c;在用表中的成绩grade与得到的平均成绩一一比较 这里可以进行连表操作&#xff0c;把分组查询得到的结果与原表通过sno学号进行等值连接 &#xff0c;就…

轻量级SQLite可视化工具Sqliteviz

什么是 Sqliteviz &#xff1f; Sqliteviz 是一个单页面离线优先的渐进式网络应用&#xff08;PWA&#xff09;&#xff0c;用于完全客户端的 SQLite 数据库或 CSV 文件的可视化。 所谓完全客户端&#xff0c;就是您的数据库永远不会离开您的计算机。使用 sqliteviz&#xff0c…

ubuntu server 安装emqx tdengine

文章目录 1 安装emqx2 安装taos 1 安装emqx 86 wget https://www.emqx.com/zh/downloads/broker/5.6.0/emqx-5.6.0-ubuntu22.04-amd64.deb87 ls88 pwd89 sudo apt install ./emqx-5.6.0-ubuntu22.04-amd64.deb90 sudo systemctl start emqx91 systemctl status emqx92 s…

C语言求自幂数(水仙花数与其他自幂数)

前言 今天我们来看一下如果求解自幂数&#xff08;水仙花数&#xff09;&#xff0c;水仙花数是自幂数的一种&#xff0c;我们来看看自幂数的概念吧&#xff0c;当一个n位数&#xff0c;它的每个位上的数字的n次幂之和等于它本身的时候&#xff0c;我们称这个数为自幂数。水仙花…

文本溢出体验进阶:CSS 技巧实现单行/多行隐藏展示以及实际场景应用,确保内容可读性和布局整洁性

CSS文本溢出隐藏是一种常见的场景&#xff0c;它广泛应用于各种网页设计中&#xff0c;旨在确保内容的可读性和布局的整洁性&#xff0c;特别是在空间有限或需要适应不同屏幕尺寸的情况下。 一、文本溢出隐藏并显示省略号 1、单行文本溢出隐藏并显示省略号 对于单行文本&…

怎么找程序员对象?和程序员谈恋爱是什么体验?

和程序员谈恋爱是一种非常累的体验&#xff0c;因为他们大部分时间都会加班&#xff0c;守在电脑面前不能够陪自己。但是这样的男孩子&#xff0c;忠诚、认真、不会花花草草、收入贼高&#xff0c;恋爱幸福指数接近满分哦&#xff01; 程序员都是宅男&#xff0c;都是电脑狂魔&…

中科院JCR期刊分区介绍

文章目录 1. 背景2. 简介3. 学科分类方法4. 分区表计算方法5. 分区指标说明5.1 IF5.2 3年平均IF5.3 CI 6. 中科院分区和JCR期刊分区有哪些异同&#xff1f;6.1 数据基础相同6.2 学科划分小类部分相同 1. 背景 SCI作为论文与引文分析的重要手段, 被国内各级科研管理部门所重视,…