前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >江哥带你玩转C语言 | 17-文件操作

江哥带你玩转C语言 | 17-文件操作

原创
作者头像
极客江南
修改于 2021-07-12 02:27:06
修改于 2021-07-12 02:27:06
1.1K00
代码可运行
举报
文章被收录于专栏:代码情缘代码情缘
运行总次数:0
代码可运行

文件基本概念

  • 文件流:
    • C 语言把文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因此 c 语言将文件也称之为文件流。
  • 文件分类
    • 文本文件
      • 以 ASCII 码格式存放,一个字节存放一个字符文本文件的每一个字节存放一个 ASCII 码,代表一个字符。这便于对字符的逐个处理,但占用存储空间 较多,而且要花费时间转换。
      • .c文件就是以文本文件形式存放的
    • 二进制文件
      • 以补码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间较少。数据按其内存中的存储形式原样存放
      • .exe文件就是以二进制文件形式存放的

  • 文本文件和二进制文件示例
    • 下列代码暂时不要求看懂, 主要理解什么是文本文件什么是二进制文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    /*
     * 以文本形式存储
     * 会将每个字符先转换为对应的ASCII,
     * 然后再将ASCII码的二进制存储到计算机中
     */
    int num = 666;
    FILE *fa = fopen("ascii.txt", "w");
    fprintf(fa, "%d", num);
    fclose(fa);

    /*
     * 以二进制形式存储
     * 会将666的二进制直接存储到文件中
     */
    FILE *fb = fopen("bin.txt", "w");
    fwrite(&num, 4, 1, fb);
    fclose(fb);

    return 0;
}
  • 内存示意图
  • 通过文本工具打开示意图
  • 文本工具默认会按照ASCII码逐个直接解码文件, 由于文本文件存储的就是ASCII码, 所以可以正常解析显示, 由于二进制文件存储的不是ASCII码, 所以解析出来之后是乱码

文件的打开和关闭

  • FILE 结构体
    • FILE 结构体是对缓冲区和文件读写状态的记录者,所有对文件的操作,都是通过FILE 结构体完成的。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  struct _iobuf {
    char *_ptr;  //文件输入的下一个位置
    int _cnt;  //当前缓冲区的相对位置
    char *_base; //文件的起始位置)
    int _flag; //文件标志
    int _file;  //文件的有效性验证
    int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
    int _bufsiz; // 缓冲区大小
    char *_tmpfname; //临时文件名
  };
  typedef struct _iobuf FILE;

  • fileopen函数

函数声明

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

所在文件

stdio.h

函数功能

以 mode 的方式,打开一个 filename 命名的文件,返回一个指向该文件缓冲的 FILE 结构体指针。

参数及返回解析

参数

char*filaname :要打开,或是创建文件的路径。

参数

char*mode :打开文件的方式。

返回值

FILE* 返回指向文件缓冲区的指针,该指针是后序操作文件的句柄。

mode

处理方式

当文件不存在时

当文件存在时

向文件输入

从文件输出

r

读取

出错

打开文件

不能

可以

w

写入

建立新文件

覆盖原有文件

可以

不能

a

追加

建立新文件

在原有文件后追加

可以

不能

r+

读取/写入

出错

打开文件

可以

可以

w+

写入/读取

建立新文件

覆盖原有文件

可以

可以

a+

读取/追加

建立新文件

在原有文件后追加

可以

可以

注意点:

  • Windows如果读写的是二进制文件,则还要加 b,比如 rb, r+b 等。 unix/linux 不区分文本和二进制文件

  • fclose函数

函数声明

int fclose ( FILE * stream );

所在文件

stdio.h

函数功能

fclose()用来关闭先前 fopen()打开的文件.

函数功能

此动作会让缓冲区内的数据写入文件中, 并释放系统所提供的文件资源

参数及返回解析

参数

FILE* stream :指向文件缓冲的指针。

返回值

int 成功返回 0 ,失败返回 EOF(-1)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    fclose(fp);
    return 0;
}

一次读写一个字符

  • 写入

函数声明

int fputc (int ch, FILE * stream );

所在文件

stdio.h

函数功能

将 ch 字符,写入文件。

参数及返回解析

参数

FILE* stream :指向文件缓冲的指针。

参数

int : 需要写入的字符。

返回值

int 写入成功,返回写入成功字符,如果失败,返回 EOF。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    // 1.打开一个文件
    FILE *fp = fopen("test.txt", "w+");

    // 2.往文件中写入内容
    for(char ch = 'a'; ch <= 'z'; ch++){
        // 一次写入一个字符
        char res = fputc(ch, fp);
        printf("res = %c\n", res);
    }

    // 3.关闭打开的文件
    fclose(fp);
    return 0;
}
  • 读取

函数声明

int fgetc ( FILE * stream );

所在文件

stdio.h

函数功能

从文件流中读取一个字符并返回。

参数及返回解析

参数

FILE* stream :指向文件缓冲的指针。

返回值

int 正常,返回读取的字符;读到文件尾或出错时,为 EOF。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    // 1.打开一个文件
    FILE *fp = fopen("test.txt", "r+");

    // 2.从文件中读取内容
    char res = EOF;
    while((res = fgetc(fp)) != EOF){
        printf("res = %c\n", res);
    }

    // 3.关闭打开的文件
    fclose(fp);
    return 0;
}
  • 判断文件末尾
    • feof函数

函数声明

int feof( FILE * stream );

所在文件

stdio.h

函数功能

判断文件是否读到文件结尾

参数及返回解析

参数

FILE* stream :指向文件缓冲的指针。

返回值

int 0 未读到文件结尾,非零 读到文件结尾。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    // 1.打开一个文件
    FILE *fp = fopen("test.txt", "r+");

    // 2.从文件中读取内容
    char res = EOF;
    // 注意: 由于只有先读了才会修改标志位,
    // 所以通过feof判断是否到达文件末尾, 一定要先读再判断, 不能先判断再读
    while((res = fgetc(fp)) && (!feof(fp))){
        printf("res = %c\n", res);
    }

    // 3.关闭打开的文件
    fclose(fp);
    return 0;
}
  • 注意点:
  • feof 这个函数,是去读标志位判断文件是否结束的。
  • 而标志位只有读完了才会被修改, 所以如果先判断再读标志位会出现多打一次的的现象
  • 所以企业开发中使用feof函数一定要先读后判断, 而不能先判断后读
  • 作业
    • 实现文件的简单加密和解密
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
void encode(char *name, char *newName, int code);
void decode(char *name, char *newName, int code);
int main()
{
    encode("main.c", "encode.c", 666);
    decode("encode.c", "decode.c", 666);
    return 0;
}
/**
 * @brief encode 加密文件
 * @param name 需要加密的文件名称
 * @param newName 加密之后的文件名称
 * @param code 秘钥
 */
void encode(char *name, char *newName, int code){
    FILE *fw = fopen(newName, "w+");
    FILE *fr = fopen(name, "r+");
    char ch = EOF;
    while((ch = fgetc(fr)) && (!feof(fr))){
        fputc(ch ^ code, fw);
    }
    fclose(fw);
    fclose(fr);
}
/**
 * @brief encode 解密文件
 * @param name 需要解密的文件名称
 * @param newName 解密之后的文件名称
 * @param code 秘钥
 */
void decode(char *name, char *newName, int code){
    FILE *fw = fopen(newName, "w+");
    FILE *fr = fopen(name, "r+");
    char ch = EOF;
    while((ch = fgetc(fr)) && (!feof(fr))){
        fputc(ch ^ code, fw);
    }
    fclose(fw);
    fclose(fr);
}

一次读写一行字符

  • 什么是行
  • 行是文本编辑器中的概念,文件流中就是一个字符。这个在不同的平台是有差异的。window 平台 ‘\r\n’,linux 平台是’\n’
  • 平台差异
    • windows 平台在写入’\n’是会体现为’\r\n’,linux 平台在写入’\n’时会体现为’\n’。windows 平台在读入’\r\n’时,体现为一个字符’\n’,linux 平台在读入’\n’时,体现为一个字符’\n’
    • linux 读 windows 中的换行,则会多读一个字符,windows 读 linux 中的换行,则没有问题
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fw = fopen("test.txt", "w+");
    fputc('a', fw);
    fputc('\n', fw);
    fputc('b', fw);
    fclose(fw);
    return 0;
}

  • 写入一行

函数声明

int fputs(char *str,FILE *fp)

所在文件

stdio.h

函数功能

把 str 指向的字符串写入 fp 指向的文件中。

参数及返回解析

参数

char * str : 表示指向的字符串的指针。

参数

FILE *fp : 指向文件流结构的指针。

返回值

int 正常,返 0;出错返 EOF。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fw = fopen("test.txt", "w+");
    // 注意: fputs不会自动添加\n
    fputs("lnj\n", fw);
    fputs("it666\n", fw);
    fclose(fw);
    return 0;
}
  • 遇到\0自动终止写入
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    // 注意: fputs写入时遇到\0就会自动终止写入
    fputs("lnj\0it666\n", fp);

    fclose(fp);
    return 0;
}

  • 读取一行

函数声明

char *fgets(char *str,int length,FILE *fp)

所在文件

stdio.h

函数功能

从 fp 所指向的文件中,至多读 length-1 个字符,送入字符数组 str 中, 如果在读入 length-1 个字符结束前遇\n 或 EOF,读入即结束,字符串读入后在最后加一个‘\0’字符。

参数及返回解析

参数

char * str :指向需要读入数据的缓冲区。

参数

int length :每一次读数字符的字数。

参数

FILE* fp :文件流指针。

返回值

char * 正常,返 str 指针;出错或遇到文件结尾 返空指针 NULL。

  • 最多只能读取N-1个字符
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    // 注意: fputs不会自动添加\n
    fputs("it666\n", fp);

    // 将FILE结构体中的读写指针重新移动到最前面
    // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
    rewind(fp);
    char str[1024];
    // 从fp中读取4个字符, 存入到str中
    // 最多只能读取N-1个字符, 会在最后自动添加\0
    fgets(str, 4, fp);

    printf("str = %s", str); // it6
    fclose(fp);
    return 0;
}
  • 遇到\n自动结束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
int main()
{
    FILE *fp = fopen("test.txt", "w+");
    // 注意: fputs不会自动添加\n
    fputs("lnj\n", fp);
    fputs("it666\n", fp);

    // 将FILE结构体中的读写指针重新移动到最前面
    // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
    rewind(fp);
    char str[1024];
    // 从fp中读取1024个字符, 存入到str中
    // 但是读到第4个就是\n了, 函数会自动停止读取
    // 注意点: \n会被读取进来
    fgets(str, 1024, fp);

    printf("str = %s", str); // lnj
    fclose(fp);
    return 0;
}
  • 读取到EOF自动结束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    // 注意: fputs不会自动添加\n
    fputs("lnj\n", fp);
    fputs("it666", fp);

    // 将FILE结构体中的读写指针重新移动到最前面
    // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
    rewind(fp);
    char str[1024];
    // 每次从fp中读取1024个字符, 存入到str中
    // 读取到文件末尾自动结束
    while(fgets(str, 1024, fp)){
        printf("str = %s", str);
    }
    fclose(fp);
    return 0;
}
  • 注意点:
    • 企业开发中能不用feof函数就不用feof函数
    • 如果最后一行,没有行‘\n’的话则少读一行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    // 注意: fputs不会自动添加\n
    fputs("12345678910\n", fp);
    fputs("12345678910\n", fp);
    fputs("12345678910", fp);

    // 将FILE结构体中的读写指针重新移动到最前面
    // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
    rewind(fp);
    char str[1024];
    // 每次从fp中读取1024个字符, 存入到str中
    // 读取到文件末尾自动结束
    while(fgets(str, 1024, fp) && !feof(fp)){
        printf("str = %s", str);
    }
    fclose(fp);
    return 0;
}
  • 作业:
    • 利用fgets(str, 5, fp)读取下列文本会读取多少次?
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
12345678910
12345
123

一次读写一块数据

  • C 语言己经从接口的层面区分了,文本的读写方式和二进制的读写方式。前面我们讲的是文本的读写方式。
  • 所有的文件接口函数,要么以 ‘\0’,表示输入结束,要么以 ‘\n’, EOF(0xFF)表示读取结束。 ‘\0’ ‘\n’ 等都是文本文件的重要标识,而所有的二进制接口对于这些标识,是不敏感的。 +二进制的接口可以读文本,而文本的接口不可以读二进制
  • 一次写入一块数据

函数声明

int fwrite(void *buffer, int num_bytes, int count, FILE *fp)

所在文件

stdio.h

函数功能

把buffer 指向的数据写入fp 指向的文件中

参数

char * buffer : 指向要写入数据存储区的首地址的指针

int num_bytes: 每个要写的字段的字节数count

int count : 要写的字段的个数

FILE* fp : 要写的文件指针

返回值

int 成功,返回写的字段数;出错或文件结束,返回 0。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp = fopen("test.txt", "wb+");
    // 注意: fwrite不会关心写入数据的格式
    char *str = "lnj\0it666";
     /*
     * 第一个参数: 被写入数据指针
     * 第二个参数: 每次写入多少个字节
     * 第三个参数: 需要写入多少次
     * 第四个参数: 已打开文件结构体指针
     */
    fwrite((void *)str, 9, 1, fp);

    fclose(fp);
    return 0;
}
  • 一次读取一块数据

函数声明

int fread(void *buffer, int num_bytes, int count, FILE *fp)

所在文件

stdio.h

函数功能

把fp 指向的文件中的数据读到 buffer 中。

参数

char * buffer : 指向要读入数据存储区的首地址的指针

int num_bytes: 每个要读的字段的字节数count

int count : 要读的字段的个数

FILE* fp : 要读的文件指针

返回值

int 成功,返回读的字段数;出错或文件结束,返回 0。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    // test.txt中存放的是"lnj\0it666"
    FILE *fr = fopen("test.txt", "rb+");
    char buf[1024] = {0};
    // fread函数读取成功返回读取到的字节数, 读取失败返回0
    /*
     * 第一个参数: 存储读取到数据的容器
     * 第二个参数: 每次读取多少个字节
     * 第三个参数: 需要读取多少次
     * 第四个参数: 已打开文件结构体指针
     */ 
    int n = fread(buf, 1, 1024, fr);
    printf("%i\n", n);
    for(int i = 0; i < n; i++){
        printf("%c", buf[i]);
    }
    fclose(fr);
    return 0;
}
  • 注意点:
  • 读取时num_bytes应该填写读取数据类型的最小单位, 而count可以随意写
  • 如果读取时num_bytes不是读取数据类型最小单位, 会引发读取失败
  • 例如: 存储的是char类型 6C 6E 6A 00 69 74 36 36 36 如果num_bytes等于1, count等于1024, 那么依次取出 6C 6E 6A 00 69 74 36 36 36 , 直到取不到为止 如果num_bytes等于4, count等于1024, 那么依次取出[6C 6E 6A 00][69 74 36 36] , 但是最后还剩下一个36, 但又不满足4个字节, 那么最后一个36则取不到
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>

int main()
{

    // test.txt中存放的是"lnj\0it666"
    FILE *fr = fopen("test.txt", "rb+");
    char buf[1024] = {0};
    /*
    while(fread(buf, 4, 1, fr) > 0){
        printf("%c\n", buf[0]);
        printf("%c\n", buf[1]);
        printf("%c\n", buf[2]);
        printf("%c\n", buf[3]);
    }
    */
    /*
    while(fread(buf, 1, 4, fr) > 0){
        printf("%c\n", buf[0]);
        printf("%c\n", buf[1]);
        printf("%c\n", buf[2]);
        printf("%c\n", buf[3]);
    }
    */
    while(fread(buf, 1, 1, fr) > 0){
        printf("%c\n", buf[0]);
    }
    fclose(fr);
    return 0;
}
  • 注意: fwrite和fread本质是用来操作二进制的
  • 所以下面用法才是它们的正确打开姿势
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{

    FILE *fp = fopen("test.txt", "wb+");
    int ages[4] = {1, 3, 5, 6};
    fwrite(ages, sizeof(ages), 1, fp);
    rewind(fp);
    int data;
    while(fread(&data, sizeof(int), 1, fp) > 0){
        printf("data = %i\n", data);
    }
    return 0;
}

读写结构体

  • 结构体中的数据类型不统一,此时最适合用二进制的方式进行读写
  • 读写单个结构体
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

typedef struct{
    char *name;
    int age;
    double height;
} Person;

int main()
{
    Person p1 = {"lnj", 35, 1.88};
//    printf("name = %s\n", p1.name);
//    printf("age = %i\n", p1.age);
//    printf("height = %lf\n", p1.height);

    FILE *fp = fopen("person.stu", "wb+");
    fwrite(&p1, sizeof(p1), 1, fp);

    rewind(fp);
    Person p2;
    fread(&p2, sizeof(p2), 1, fp);
    printf("name = %s\n", p2.name);
    printf("age = %i\n", p2.age);
    printf("height = %lf\n", p2.height);

    return 0;
}
  • 读写结构体数组
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

typedef struct{
    char *name;
    int age;
    double height;
} Person;

int main()
{
    Person ps[] = {
      {"zs", 18, 1.65},
      {"ls", 21, 1.88},
      {"ww", 33, 1.9}
    };


    FILE *fp = fopen("person.stu", "wb+");
    fwrite(&ps, sizeof(ps), 1, fp);

    rewind(fp);
    Person p;
    while(fread(&p, sizeof(p), 1, fp) > 0){
        printf("name = %s\n", p.name);
        printf("age = %i\n", p.age);
        printf("height = %lf\n", p.height);
    }
    return 0;
}
  • 读写结构体链表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>

typedef struct person{
    char *name;
    int age;
    double height;
    struct person* next;
} Person;
Person *createEmpty();
void  insertNode(Person *head, char *name, int age, double height);
void printfList(Person *head);
int saveList(Person *head, char *name);
Person *loadList(char *name);

int main()
{

//    Person *head = createEmpty();
//    insertNode(head, "zs", 18, 1.9);
//    insertNode(head, "ls", 22, 1.65);
//    insertNode(head, "ws", 31, 1.78);
//    printfList(head);
//    saveList(head, "person.list");
    Person *head = loadList("person.list");
    printfList(head);
    return 0;
}

/**
 * @brief loadList 从文件加载链表
 * @param name 文件名称
 * @return  加载好的链表头指针
 */
Person *loadList(char *name){
    // 1.打开文件
    FILE *fp = fopen(name, "rb+");
    if(fp == NULL){
        return NULL;
    }
    // 2.创建一个空链表
    Person *head = createEmpty();
    // 3.创建一个节点
    Person *node = (Person *)malloc(sizeof(Person));
    while(fread(node, sizeof(Person), 1, fp) > 0){
        // 3.进行插入
        // 3.1让新节点的下一个节点 等于 头节点的下一个节点
        node->next = head->next;
        // 3.2让头结点的下一个节点 等于 新节点
        head->next = node;

        // 给下一个节点申请空间
        node = (Person *)malloc(sizeof(Person));
    }
    // 释放多余的节点空间
    free(node);
    fclose(fp);
    return head;
}

/**
 * @brief saveList 存储链表到文件
 * @param head 链表头指针
 * @param name 存储的文件名称
 * @return  是否存储成功 -1失败 0成功
 */
int saveList(Person *head, char *name){
    // 1.打开文件
    FILE *fp = fopen(name, "wb+");
    if(fp == NULL){
        return -1;
    }
    // 2.取出头节点的下一个节点
    Person *cur = head->next;
    // 3.将所有有效节点保存到文件中
    while(cur != NULL){
        fwrite(cur, sizeof(Person), 1, fp);
        cur = cur->next;
    }
    fclose(fp);
    return 0;
}
/**
 * @brief printfList 遍历链表
 * @param head 链表的头指针
 */
void printfList(Person *head){
    // 1.取出头节点的下一个节点
    Person *cur = head->next;
    // 2.判断是否为NULL, 如果不为NULL就开始遍历
    while(cur != NULL){
        // 2.1取出当前节点的数据, 打印
        printf("name = %s\n", cur->name);
        printf("age = %i\n", cur->age);
        printf("height = %lf\n", cur->height);
        printf("next = %x\n", cur->next);
        printf("-----------\n");
        // 2.2让当前节点往后移动
        cur = cur->next;
    }
}

/**
 * @brief insertNode 插入新的节点
 * @param head 链表的头指针
 * @param p 需要插入的结构体
 */
void  insertNode(Person *head, char *name, int age, double height){
    // 1.创建一个新的节点
    Person *node = (Person *)malloc(sizeof(Person));
    // 2.将数据保存到新节点中
    node->name = name;
    node->age = age;
    node->height = height;

    // 3.进行插入
    // 3.1让新节点的下一个节点 等于 头节点的下一个节点
    node->next = head->next;
    // 3.2让头结点的下一个节点 等于 新节点
    head->next = node;
}
/**
 * @brief createEmpty 创建一个空链表
 * @return 链表头指针, 创建失败返回NULL
 */
Person *createEmpty(){
    // 1.定义头指针
    Person *head = NULL;
    // 2.创建一个空节点, 并且赋值给头指针
    head = (Person *)malloc(sizeof(Person));
    if(head == NULL){
        return head;
    }
    head->next = NULL;
    // 3.返回头指针
    return head;
}

其它文件操作函数

  • ftell 函数

函数声明

long ftell ( FILE * stream );

所在文件

stdio.h

函数功能

得到流式文件的当前读写位置,其返回值是当前读写位置偏离文件头部的字节数.

参数及返回解析

参数

FILE * 流文件句柄

返回值

int 成功,返回当前读写位置偏离文件头部的字节数。失败, 返回-1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    char *str = "123456789";
    FILE *fp = fopen("test.txt", "w+");
    long cp = ftell(fp);
    printf("cp = %li\n", cp); // 0
    // 写入一个字节
    fputc(str[0], fp);
    cp = ftell(fp);
    printf("cp = %li\n", cp); // 1
    fclose(fp);
    return 0;
}
  • rewind 函数

函数声明

void rewind ( FILE * stream );

所在文件

stdio.h

函数功能 将文件指针重新指向一个流的开头。

参数及返回解析

参数

FILE * 流文件句柄

返回值

void 无返回值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    char *str = "123456789";
    FILE *fp = fopen("test.txt", "w+");
    long cp = ftell(fp);
    printf("cp = %li\n", cp); // 0
    // 写入一个字节
    fputc(str[0], fp);
    cp = ftell(fp);
    printf("cp = %li\n", cp); // 1
    // 新指向一个流的开头
    rewind(fp);
    cp = ftell(fp);
    printf("cp = %li\n", cp); // 0
    fclose(fp);
    return 0;
}
  • fseek 函数

函数声明

int fseek ( FILE * stream, long offset, int where);

所在文件

stdio.h

函数功能

偏移文件指针。

参数及返回解析

参 数

FILE * stream 文件句柄

long offset 偏移量

int where 偏移起始位置

返回值

int 成功返回 0 ,失败返回-1

  • 常用宏
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define SEEK_CUR 1 当前文字
#define SEEK_END 2 文件结尾
#define SEEK_SET 0 文件开头
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp = fopen("test.txt", "w+");
    fputs("123456789", fp);
    // 将文件指针移动到文件结尾, 并且偏移0个单位
    fseek(fp, 0, SEEK_END);
    int len = ftell(fp); // 计算文件长度
    printf("len = %i\n", len);
    fclose(fp);
    return 0;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main()
{
    FILE *fp;
   fp = fopen("file.txt","w+");
   fputs("123456789", fp);

   fseek( fp, 7, SEEK_SET );
   fputs("lnj", fp);
   fclose(fp);
    return 0;
}

如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是江哥持续更新的动力

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
SpringBoot - 构建监控体系02_定义度量指标和 Actuator 端点
SpringBoot - 构建监控体系01_使用 Actuator 组件实现及扩展系统监控 我们引入了 Spring Boot Actuator 组件来满足 Spring Boot 应用程序的系统监控功能,并重点介绍了如何扩展常见的 Info 和 Health 监控端点的实现方法。
小小工匠
2021/08/17
9890
SpringBoot - 构建监控体系02_定义度量指标和 Actuator 端点
基于Prometheus搭建SpringCloud全方位立体监控体系
最近公司在联合运维做一套全方位监控的系统,应用集群的技术栈是SpringCloud体系。虽然本人没有参与具体基础架构的研发,但是从应用引入的包和一些资料的查阅大致推算出具体的实现方案,这里做一次推演,详细记录一下整个搭建过程。
Throwable
2020/06/23
2.7K0
Spring Boot整合 Prometheus
Micrometer 为 Java 平台上的性能数据收集提供了一个通用的 API,应用程序只需要使用 Micrometer 的通用 API 来收集性能指标即可。Micrometer 会负责完成与不同监控系统的适配工作。这就使得切换监控系统变得很容易。Micrometer 还支持推送数据到多个不同的监控系统。Micrometer类似日志系统中SLF4J。
BUG弄潮儿
2021/08/13
1.5K0
可观测性神器之 Micrometer
对于大部分开发人员来说可能用过普罗米修斯Grafana这样的监控系统,从未听说过Micrometer工具,这里就详细的来介绍下可观测性神器Micrometer,让你在开发时使用它就和使用SLFJ 日志系统一样简单易用,有效的提升系统的健壮性和可靠性。
宋小生
2022/12/05
1.8K0
可观测性神器之 Micrometer
聊聊micrometer的HistogramGauges
针对springboot应用,配备有各种export的AutoConfiguration,详见org.springframework.boot.actuate.autoconfigure.metrics.export包,2.0.1版本目前支持了如下类型的export:
code4it
2018/09/17
1.8K0
SpringBoot+Prometheus:微服务开发中自定义业务监控指标的几点经验
从马楠的上一篇文章中,我们已经了解到Prometheus的一大优势,是可以在应用内定义自己的指标做监控。我们在 SpringBoot 做微服务的生产环境中,使用自定义指标监控诸多物联网传感器,时序数据结构简单清晰,监控与统计反应迅捷,效果良好。
王录华
2019/07/31
16.2K0
SpringBoot+Prometheus:微服务开发中自定义业务监控指标的几点经验
Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
哎_小羊
2019/09/18
10.6K0
Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能
micrometer自定义metrics
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java
code4it
2018/09/17
2.9K0
实战|如何优雅地自定义Prometheus监控指标
大家好!我是"无敌码农",今天要和大家分享的是在实际工作中“如何优雅地自定义Prometheus监控指标”!目前大部分使用Spring Boot构建微服务体系的公司,大都在使用Prometheus来构建微服务的度量指标(Metrics)类监控系统。而一般做法是通过在微服务应用中集成Prometheus指标采集SDK,从而使得Spring Boot暴露相关Metrics采集端点来实现。
用户5927304
2021/04/14
2.1K0
SpringBoot Actuator — 埋点和监控
监控机器环境的性能和业务流程或逻辑等各项数据,并根据这些数据生成对应的指标,那么我们就称为数据埋点。比如我们想知道某个接口调用的 TPS、机器 CPU 的使用率,这些都可以用到数据埋点
晚上没宵夜
2021/11/16
1.4K0
【监控利器Prometheus】——Prometheus+Grafana监控SpringBoot项目业务指标监控
(3)这里以【订单成功数量】、【订单失败数量】、【订单成功金额】、【订单失败金额】为例进行统计
DannyHoo
2021/12/23
1.3K0
【监控利器Prometheus】——Prometheus+Grafana监控SpringBoot项目业务指标监控
聊聊ExecutorService的监控
metrics-core-4.0.2-sources.jar!/com/codahale/metrics/InstrumentedExecutorService.java
code4it
2018/10/18
1.3K0
Springboot2 Metrics之actuator集成influxdb, Grafana提供监控和报警
到目前为止,各种日志收集,统计监控开源组件数不胜数,即便如此还是会有很多人只是tail -f查看一下日志文件。随着容器化技术的成熟,日志和metrics度量统计已经不能仅仅靠tail -f来查看了,你甚至都不能进入部署的机器。因此,日志收集和metrics统计就必不可少。日志可以通过logstash或者filebeat收集到ES中用来查阅。对于各种统计指标,springboot提供了actuator组件,可以对cpu, 内存,线程,request等各种指标进行统计,并收集起来。本文将粗略的集成influxdb来实现数据收集,以及使用Grafana来展示。
Ryan-Miao
2019/06/22
2.2K0
Prometheus + Grafana 监控 SpringBoot
Prometheus 是监控系统,可以从 Springboot 获取监控数据,以时序数据的形式存储,并提供了监控数据的查询服务。
dys
2020/02/19
2K0
聊聊springboot的TomcatMetricsBinder
org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java
code4it
2023/10/30
2820
聊聊springboot的TomcatMetricsBinder
手把手教你实现SpringBoot微服务监控!
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/03/04
4.6K0
聊聊springboot的TomcatMetricsBinder
org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java
code4it
2023/10/27
2000
聊聊springboot1.x及2.x的JvmGcMetrics的区别
本文主要研究一下springboot1.x及2.x的JvmGcMetrics的区别
code4it
2018/09/17
1.2K0
通过micrometer实时监控线程池的各项指标
最近的一个项目中涉及到文件上传和下载,使用到JUC的线程池ThreadPoolExecutor,在生产环境中出现了某些时刻线程池满负载运作,由于使用了CallerRunsPolicy拒绝策略,导致满负载情况下,应用接口调用无法响应,处于假死状态。考虑到之前用micrometer + prometheus + grafana搭建过监控体系,于是考虑使用micrometer做一次主动的线程池度量数据采集,最终可以相对实时地展示在grafana的面板中。
Throwable
2020/06/23
4.5K0
聊聊springboot2的micrometer
springboot2在spring-boot-actuator中引入了micrometer,对1.x的metrics进行了重构,另外支持对接的监控系统也更加丰富(Atlas、Datadog、Ganglia、Graphite、Influx、JMX、NewRelic、Prometheus、SignalFx、StatsD、Wavefront)。1.x的metrics都有点对齐dropwizard-metrics的味道,而micrometer除了一些基本metrics与dropwizard-metrics相类似外,重点支持了tag。这是一个很重要的信号,标志着老一代的statsd、graphite逐步让步于支持tag的influx以及prometheus。
code4it
2018/09/17
2.2K0
推荐阅读
相关推荐
SpringBoot - 构建监控体系02_定义度量指标和 Actuator 端点
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档