C文件

linux中一切皆文件

 
Linux 系统中,把一切都看做是文件,
当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,

文件描述符就是 内核为了高效 管理已被打开的文件所创建的 索引(0,1,2,...),
用来指向被打开的文件,

通过文件描述符 执行I/O系统调用

文件描述符、文件、进程间的关系

 
每个文件描述符对应一个打开的文件;

同一文件可以被相同/不同的进程多次打开,每打开一次,都会建立一个文件描述符

不同的文件描述符可能指向同一个文件
  

父子进程同时打开一个文件

 
#include <stdio.h>
#include <sys/wait.h>

int main(){

    const char* pathname = "/tmp/a.log";
    int f1 =  open(pathname);
    int rc=fork();
    if(rc==0){
        int f2 =  open(pathname);
        printf("child  file index:%d\n",f2);
        close(f2);
        
    }else{
        printf("father file index:%d\n",f1);
        close(f1);
        wait(NULL);
    }

    return 0;
}

输出:
father file index:3
child  file index:4

从3开始向后排列,0,1,2被STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO占用

有两套API

 
linux 内核:open,read,write 

C API: fopen,fread,fwrite 

fread相比read增加了缓冲功能,
后台还是调用的read,只是减少了与内核交互的次数 

C目录或文件是否存在

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

//判断文件或目录是否存在
bool exists(const char* filepath){
    struct stat st;
    if (stat(filepath, &st) == 0) {
        return 1;
    }
    return 0;
}

int main(){
    const char* filename = "/tmp/need2"; // 要获取创建时间的文件名
    bool res = exists(filename);
    if(res){
        printf("文件存在\n");
    }else{
        printf("文件不存在\n");
    }
    return 0;
}

目录还是文件判断

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

int main(int argc, char const *argv[])
{
    char const*path = argv[1];

    struct stat fil_info;
    stat(path, &fil_info);

    if(S_ISDIR(fil_info.st_mode)){
        printf("dir\n");
    }
    if(S_ISREG(fil_info.st_mode)){
        printf("file\n");
    }

    return 0;
}

遍历

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


int show_file(const char* path){

    struct stat fil_info;
    /*获取文件信息,把信息放到s_buf中*/
    stat(path, &fil_info);

    if(S_ISDIR(fil_info.st_mode))
    {
        printf("[%s] it is a dir\n",path);
        struct dirent *filename;
        DIR *dp = opendir(path);

        /*readdir()必须循环调用,要读完整个目录的文件,readdir才会返回NULL
        若未读完,就让他循环*/
        while(filename = readdir(dp))
        {
            /*判断一个文件是目录还是一个普通文件*/
            char file_path[200];
            bzero(file_path,200);

            strcat(file_path,path);
            strcat(file_path,"/");
            strcat(file_path,filename->d_name);

            /*获取文件信息,把信息放到fil_info中*/
            stat(file_path,&fil_info);

            /*判断是否目录*/
            if(S_ISDIR(fil_info.st_mode))
            {
                printf("[%s] is a dir\n",file_path);
            }

            /*判断是否为普通文件*/
            if(S_ISREG(fil_info.st_mode))
            {
                printf("[%s] is a regular file\n",file_path);
            }
        }
    }else if(S_ISREG(fil_info.st_mode))
    {
        printf("[%s] is a regular file\n",path);
        return 0;
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    char const*path = argv[1];
    show_file(path);

    return 0;
}

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

void tobin(int n){
    int arr[32] = { 0 };//int 型有32个字节
    int i = 0;
    while (n > 0)
    {
        arr[i] = n % 2;
        ++i;
        n /= 2;
    }
    int k = 0;
    for (k = i-1; k >= 0; k--) //逆序输出
    {
            printf("%d", arr[k]);
    }
}

int main()
{
    char* fileName = "/tmp/a.log";
    struct stat buf;
    int result;

    printf("S_IFDIR:%d\n",S_IFDIR);          //S_IFDIR:16384
    printf("buf.st_mode:%d\n",buf.st_mode);  //buf.st_mode:17407

    printf("S_IFDIR & buf.st_mode:%d\n",S_IFDIR & buf.st_mode);  //S_IFDIR & buf.st_mode:16384

    printf("S_IFREG:%d\n",S_IFREG);  // S_IFREG:32768
    printf("S_IFREG & buf.st_mode:%d\n",S_IFREG & buf.st_mode);  //S_IFREG & buf.st_mode:0

    tobin(S_IFDIR);    //0100000000000000
    printf("\n");
    tobin(buf.st_mode);//0100001111111111
    printf("\n");
    tobin(S_IFREG);    //1000000000000000
    printf("\n");

    result = stat(fileName, &buf);
    if(S_IFDIR & buf.st_mode){
    printf("folder\n");
    }else if(S_IFREG & buf.st_mode){
    printf("file\n");
    }

    return 0;
}

C读取目录下文件

 
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <time.h>


//读取文件列表
void read_dir_files(char *path)
{
    //打开目录是一瞬间的
    DIR *dir = opendir(path);//打开目录文件
    printf("open dir %s\n","----"); 

    struct dirent *entry;

    int count = 0;

    // 不会递归读取子目录
    while((entry = readdir(dir))!=0)
    {
        //d_name是文件名称,包含后缀,不包含父目录路径,
        if(strstr(entry->d_name,".txt"))
        {
            printf("txt file: %s\n", entry->d_name);
        }else{
            printf("%s\n", entry->d_name);
        }
        count++;
        printf("read file count %d\n",count); 
    }
    closedir(dir);
}

int main(){

    time_t start,end;

    // 返回从1970年1月1日00:00:00到现在经过的秒数
    start = time(NULL);

    char *file_dir = "/MTCNN_MY/data/data_tmp/MTCNN/24/positive";
    read_dir_files(file_dir);

    end = time(NULL);

    // 55459张图片, time = 22.000000
    printf("time = %f\n", difftime(end, start));
    return 0;
}

 
这里读取目录是流式的,即读取一个目录下的文件,就可以获取这个文件的名称
不是一次性读取所有文件,然后再从第一个文件开始告诉你它的名称是什么
当一个目录下的文件数量万级以上时,这二者差别很大 

输出使用了流,IO是共用的,是共享资源,谁有需求谁就可以用一下,
是共享资源它就少,你用的时候刚好有别的程序也在用,那么你就得等待一下 
所以输出并不是从第一个循环就有的
跟红绿灯下人群过马路类似,积累一波缓冲输出一下

    

常用文件读写标识

 
w+ 可读可写,写时覆盖
a+ 可读可写,写时追加
r  读文件,文件若不存在直接报错
b  二进制文件 

 

    
C字符读写·读取文件全部内容

fgetc:一个字符一个字符地读

 
#include <stdio.h> 

int main(void){
    int ch;
    FILE *fp;
    char fname[FILENAME_MAX];
    
    printf("文件名:");
    scanf("%s", fname);
    
    if((fp = fopen(fname, "r")) == NULL){
        printf("文件打开失败。\n");
    } else {
        while ((ch = fgetc(fp)) != EOF){
            putchar(ch);
        }
        fclose(fp);
    }
    
    return 0;
} 

int fgetc(FILE *stream);
从stream指向的输入流(若存在)中读取unsigned char型的下一个字符的值,并将它转换为int型,
然后,若定义了流的文件位置指示符,则将其向前移动。

若在流中检查到文件末尾,则设置该流的文件结果指示符并返回EOF。

C一次读取文件所有内容

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

char* readAll(const char* filename) {
    char* data;
    FILE* fp = fopen(filename, "r");
    fseek(fp, 0, SEEK_END);
    int num_int = (int)ftell(fp);
    data = (char*)malloc(num_int * sizeof(char));
    memset(data, 0, num_int);
    fseek(fp, 0, SEEK_SET);
    int size_int = sizeof(int);
    fread(data, size_int, num_int, fp);
    fclose(fp);
    return data;
}
int main() {
    printf("%s", readAll("/tmp/a.log"));
    return 0;
}


C字符串读写

数字,字符串读写

 
#include <stdio.h> 

int main()
{
    FILE *fp = NULL;
    fp = fopen("/tmp/a.log", "w+");   // w+ 表示可读可写
    fprintf(fp, "%d: ",1);            // 格式化输入,要输入什么类型先指定格式
    fprintf(fp, "每天 学习 五分钟\n");  // 不指定默认为字符串
    fputs("2: 每天 运动 半小时\n", fp); // 专写字符串
    fclose(fp);

    char buff[255];
    
    fp = fopen("/tmp/a.log", "r");
    fscanf(fp, "%s", buff);      //遇到空格或换行符时,停止读取
    printf("%s\n", buff );       //1:
    
    fgets(buff, 255, (FILE*)fp); //读一行或255个字符
    printf("%s\n", buff );       //  每天 学习 五分钟
    
    fgets(buff, 255, (FILE*)fp); 
    printf("3: %s\n", buff );    //2: 每天 运动 半小时
    fclose(fp);
}

C字节读写

带缓存的字节读写


以f开头表示使用缓冲

fwrite(开始地址,一次写多少个字节,写多少次,文件句柄)
fread(开始地址,一次读多少个字节,读多少次,文件句柄)
一次读写多少个字节,读写多少次都是正整数, 定义一个新类型size_t ,表示无符号整形

由于读写N次,需要用到数组结构,
数组元素数据类型占的字节数 是第二个参数“一次读写多少个字节”的值 
数组的长度,就是第三个参数“读写多少次”的值 
数组的首地址,就是开始地址

数组在内存中以字节连接存储,
fwrite的意思就是将数组对应的字节从内存原封不动地搬到文件里,
fread是fwrite的逆向操作,原来是从内存到文件,现在就从文件返回到内存,

原来的数组放的是整数,返回后还是整数,原来放的是结构体,返回后还是原来的结构体,
由于高级语言存储的最小单位就是字节,
所以,就没有 fwrite/fread 不能存取的,一切数据就可以使用它们保存/加载

重新认识文件

 
这里指计算机中的虚拟文件系统,
文件的内容是连续的,底层都是以二进制存储,这一点是不是像数组?

只是在文件看来,没有数据结构一说,你一次存多少个字节进去,完全看你的业务需求
并不会像数组一样要求所有数据类型一致,即底层每个类型的字节数一致以方便数组操作
可以字母,汉字,英文单词混写,至于具体什么含义,怎么断句,文件并不管这个 

另一个与数组不同的是,文件是可以写入磁盘的,从这一点看,文件更像是可落盘的数组,

文件还可当作缓冲使用,数组都在内存里,
你需要的数据可以放一个定长的数组,超过长度的部分可以写入文件,
即通过 数组+文件 可控制程序使用内存的多少

在C中,复杂的数据类型,比如文件FILE类型,是一个结构体,
它有一个指针属性-当前读取地址,指向一个地址,
初始化为文件字节的开始地址,每读部分字节,当前读取地址就随之变动,
永远指向当前读到的字节,直到没有字节可读,即文件结束

文件可以很大,但磁盘上未必会有一片连续的地址,
文件可以利用多个磁盘碎片,所以一个文件可能存储在磁盘上的多个位置

rewind重定向:将文件指针重新指向文件开头

    
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3
    
int main(){
    int a[N]={1,2,3}, b[N];
    int i, size = sizeof(int);
    FILE *fp;

    // r是打开存在的文件,若文件不存在则返回NULL;b表示二进制,w表示只写,w+表示写+读
    if( (fp=fopen("/tmp/a.txt", "wb+")) == NULL ){  //以二进制方式打开一个文本文件
        puts("open file error!");
        exit(0);
    }

    //将数组a的内容写入到文件
    int la = sizeof(a)/sizeof(a[0]);
    printf("arr a size:%d\n",la);
    fwrite(a, size, N, fp);

    //定位到文件开头,否则后面的fread将会从文件尾部开始读取字节
    rewind(fp);
    fread(b, size, N, fp);

    for(i=0; i<N; i++){
        printf("%d ", b[i]);
    }
    printf("\n");

    fclose(fp);
    return 0;
}
C文件按字节copy

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdbool.h>

//判断文件或目录是否存在
bool exists(const char* filepath){
    struct stat st;
    if (stat(filepath, &st) == 0) {
        return 1;
    }
    return 0;
}


void tmp(){
    FILE *source_file, *destination_file, *destination_int;  
    char buffer[1024], destination_buffer[1024];  
    size_t bytes_read, bytes_written;  
        
    // 打开源文件和目标文件  
    source_file = fopen("aa.txt", "r");  
    destination_file = fopen("a22.txt", "wb"); 

    int i;

    // 按字节读取源文件并写入目标文件 
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), source_file))  > 0) {  
        printf("bytes_read:%s\n",buffer);
        fwrite(buffer, 1, bytes_read, destination_file);  
    }
        
    // 关闭文件  
    fclose(source_file);  
    fclose(destination_file);  

}
    
int main() {

    tmp();
    return 0;  
}

C块读写

读取结构体数组:一次读取指定的字节数

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3

typedef char string[30];
struct Students
{
    int    student_id;  // 4 bytes 
    string student_name;// 32 bytes
    float score;       // 4 bytes
};
int main(){
    struct Students students[N] , b[N];
    
    struct Students st1;
    st1.student_id = 1001;
    strcpy(st1.student_name,"李秋水");
    st1.score = 73;
    students[0] = st1;

    int i, size = sizeof(st1);
    printf("student1 size:%d\n",size);  // student1 size:40

    struct Students st2;
    st2.student_id = 1002;
    strcpy(st2.student_name,"韩立");
    st2.score = 74;
    students[1] = st2;

    struct Students st3;
    st3.student_id = 1003;
    strcpy(st3.student_name,"张无忌");
    st3.score = 75;
    students[2] = st3;

    FILE *fp;

    // r是打开存在的文件,若文件不存在则返回NULL;b表示二进制,w表示只写,w+表示写+读
    if( (fp=fopen("/tmp/a.txt", "wb+")) == NULL ){  //以二进制方式打开一个文本文件
        puts("open file error!");
        exit(0);
    }
    
    //将数组a的内容写入到文件
    int la = sizeof(students)/sizeof(students[0]);
    printf("arr a size:%d\n",la);
    fwrite(students, size, N, fp);

    //定位到文件开头,否则后面的fread将会从文件尾部开始读取字节
    rewind(fp);
    fread(b, size, N, fp);

    for(i=0; i<N; i++){
        printf("%s \n", b[i].student_name);
    }
    printf("\n");

    fclose(fp);
    return 0;
}
C数据块修改

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3

typedef char string[30];
struct Students
{
    int    student_id;  // 4 bytes 
    string student_name;// 32 bytes
    float score;       // 4 bytes
};

int main(){
    struct Students students[N] , b[N+1];
    
    struct Students st1;
    st1.student_id = 1001;
    strcpy(st1.student_name,"李秋水");
    st1.score = 73;
    students[0] = st1;

    int i, size = sizeof(st1);
    printf("student1 size:%d\n",size);  // student1 size:40

    struct Students st2;
    st2.student_id = 1002;
    strcpy(st2.student_name,"韩立");
    st2.score = 74;
    students[1] = st2;

    struct Students st3;
    st3.student_id = 1003;
    strcpy(st3.student_name,"张无忌");
    st3.score = 75;
    students[2] = st3;

    FILE *fp;

    // r是打开存在的文件,若文件不存在则返回NULL;b表示二进制,w表示只写,w+表示写+读
    if( (fp=fopen("/tmp/a.txt", "wb+")) == NULL ){  //以二进制方式打开一个文本文件
        puts("open file error!");
        exit(0);
    }
    
    //将数组a的内容写入到文件
    int la = sizeof(students)/sizeof(students[0]);
    printf("arr a size:%d\n",la);
    fwrite(students, size, N, fp);


    //定位到文件开头,否则后面的fread将会从文件尾部开始读取字节
    rewind(fp);
    fread(b, size, N+1, fp);
    printf("----------原数据块------------------------------\n");
    for(i=0; i<N+1; i++){
        printf("%s \n", b[i].student_name);
    }

    // 追加
    struct Students st5;
    st5.student_id = 1007;
    strcpy(st5.student_name,"江澄");
    st5.score = 82;
    fwrite(&st5, size, 1, fp);
    

    //定位到文件开头,否则后面的fread将会从文件尾部开始读取字节
    rewind(fp);

    // 覆盖
    struct Students st7;
    st7.student_id = 1007;
    strcpy(st7.student_name,"南宫婉");
    st7.score = 86;
    fwrite(&st7, size, 1, fp);

    //定位到文件开头,否则后面的fread将会从文件尾部开始读取字节
    rewind(fp);
    fread(b, size, N+1, fp);

    printf("----------修改(覆盖,追加)后的数据块------------------------\n");

    for(i=0; i<N+1; i++){
        printf("%s \n", b[i].student_name);
    }

    printf("\n");
    printf("----------局部读取,定位第2个块结束,第3个数据块开始的位置,即读取第3个数据块------------\n");

    fseek(fp,size*(3-1),SEEK_SET); 
    fread(b, size, 1, fp);
    printf("%s \n", b[0].student_name);
    
    fclose(fp);
    return 0;
}

$ gcc f1.c 
$ ./a.out 
student1 size:40
arr a size:3
----------原数据块------------------------------
李秋水 
韩立 
张无忌 

----------修改(覆盖,追加)后的数据块------------------------
南宫婉 
韩立 
张无忌 
江澄 

----------局部读取,定位第2个块结束,第3个数据块开始的位置,即读取第3个数据块------------
张无忌 

C 临时文件 tmpnam

tmpnam代码示例

 
#include <stdio.h> 

int main () {
    // 临时文件,程序运行结束就自己消失 
    // 
    printf("文件名长度:%d,最多可创建临时文件个数:%d\n",L_tmpnam,TMP_MAX);
    char buffer[L_tmpnam];
    char *ptr;
    tmpnam(buffer);
    printf("Temporary name 1: %s\n", buffer);
    puts(buffer);
    
    ptr = tmpnam(NULL);
    printf("Temporary name 2: %s\n", ptr);

    return(0);
}

$ gcc t.c 
/tmp/cc35QRUw.o: In function `main':
t.c:(.text+0x29): warning: the use of `tmpnam' is dangerous, better use `mkstemp'
$ ./a.out 
文件名长度:20,最多可创建临时文件个数:238328
Temporary name 1: /tmp/file5h3eVn
/tmp/file5h3eVn
Temporary name 2: /tmp/fileTog9xu

$ ./a.out 
文件名长度:20,最多可创建临时文件个数:238328
Temporary name 1: /tmp/fileqOMwTG
/tmp/fileqOMwTG
Temporary name 2: /tmp/file0nzEu5
        

临时文件及格式

 
临时
体现在程序运行结束后,文件自动删除;
每次运行生成的文件名称随机

格式
file+6位16进制,位于系统的临时目录下,linux的临时目录就是/tmp目录 

TMP_MAX 
最多可创建临时文件的个数 

L_tmpnam
用于存放临时文件名称的数组长度,
如果为NULL,则名称做为返回值返回

C 临时文件 mkstmp

int mkstemp(char *template)

 
mkstemp函数创建一个文件并打开,权限0600。 
函数返回一个文件描述符,如果执行失败返回-1。

unlink函数删除文件的目录入口,但还可以在代码中通过文件名称对文件进行读写操作
程序退出,临时文件自动删除

 
#include <stdio.h> 
int main(void)
{
    int fd;
    char temp_file_path[]="/tmp/tmp_XXXXXX";
    if((fd=mkstemp(temp_file_path))==-1)
    {
        printf("Creat temp file faile./n");
    }else{
        printf("temp file path:%s\n",temp_file_path);
        unlink(temp_file_path);

        //----------------------------- 
        //这里可对临时文件进行读写操作
        //----------------------------- 

        close(fd);
    }
    return 0;
}   
        

 
$ gcc t.c 
$ ./a.out 
temp file path:/tmp/tmp_LqlCqW

$ ./a.out 
temp file path:/tmp/tmp_AHrteC

C 读写CSV

C 写CSV

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

int main()
{
    char * file_name = "tmp.csv";
    FILE *fp = fopen(file_name, "w+");
    if (fp == NULL) {
        fprintf(stderr, "open %s failed.\n",file_name);
        exit(EXIT_FAILURE);
    }

    fprintf(fp, "tid,card_num,card_time\n");
    fprintf(fp, "1,62208032347824361274,2023-10-11 14:18:12\n");

    int id = 2;
    char *card_num = "62202232347824361435322";
    char *card_time = "2023-10-12 11:18:12";
    fprintf(fp, "%d,%s,%s\n", id, card_num, card_time);

    fclose(fp);
    return 0;
}

C 读CSV

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

void write_csv(){
    char * file_name = "tmp.csv";
    FILE *fp = fopen(file_name, "w+");
    if (fp == NULL) {
        fprintf(stderr, "open %s failed.\n",file_name);
        exit(EXIT_FAILURE);
    }

    fprintf(fp, "tid,card_num,card_time\n");
    fprintf(fp, "1,62208032347824361274,2023-10-11 14:18:12\n");

    int id = 2;
    char *card_num = "62202232347824361435322";
    char *card_time = "2023-10-12 11:18:12";
    fprintf(fp, "%d,%s,%s\n", id, card_num, card_time);

    fclose(fp);
}

void read_csv(char *file_name){
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL) {
        fprintf(stderr, "open %s failed.\n",file_name);
        exit(EXIT_FAILURE);
    }

    int max_size=6000;
    char row[max_size];
    char *val;
    char* tmp =NULL;
    while (fgets(row, max_size, fp) != NULL) {
        printf("Row: %s", row);
        int i = 0;

        val = strtok_r(row, ",", &tmp); 
        while (val != NULL) {
            i++;
            printf("col %d: %s\n", i,val);
            val = strtok_r(NULL, ",",&tmp);
        }
    }

    fclose(fp);
}



int main()
{
    char* file_name = "tmp.csv";
    read_csv(file_name);

    return 0;
}

C读CSV到指针数组 

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

void ss_arr(char* row, char** res){
    char* tmp=NULL;
    char* val = strtok_r(row, ",", &tmp); 
    int i=0;
    while (val != NULL) {
        res[i] = val;
        val = strtok_r(NULL, ",",&tmp);
        i++;
    }
}

int main()
{
    char row[] = "Row: 2,62202232347824361435322,2023-10-12 11:18:12";
    int max_size=100;
    //指针数组,每个元素都是一个指针
    char* res[max_size];
    ss_arr(row,res);
    int i;
    for(i=0;i<3;i++){
        printf("%s\n",res[i]);
    }
    return 0;
}

 
Row: 2
62202232347824361435322
2023-10-12 11:18:12

C 逐行读CSV

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

void ss_arr(char* row, char** res){
    char* tmp=NULL;
    char* val = strtok_r(row, ",", &tmp); 
    int i=0;
    while (val != NULL) {
        res[i] = val;
        val = strtok_r(NULL, ",",&tmp);
        i++;
    }
}

//max_col_size:拆分成多少个元素
void deal(char* row,int max_col_size){
    char* res[max_col_size];
    ss_arr(row,res);

    int i;
    for(i=0;i<max_col_size;i++){
        printf("%s\n",res[i]);
    }
}

void read_csv(char *file_name, void(*func)(char*,int),int max_col_size){
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL) {
        fprintf(stderr, "open %s failed.\n",file_name);
        exit(EXIT_FAILURE);
    }

    int max_size=6000;
    char row[max_size];
    char *val;
    char* tmp =NULL;
    while (fgets(row, max_size, fp) != NULL) {
        func(row,max_col_size);
    }
    fclose(fp);
}


int main()
{
    char* file_name = "tmp.csv";
    read_csv(file_name,deal,3);

    return 0;
}

 
tid
card_num
card_time

1
62208032347824361274
2023-10-11 14:18:12

2
62202232347824361435322
2023-10-12 11:18:12

C 读CSV带双引号

 
10022,62284800003,2022-12-19 19:10:20,1,6,1671495020.0,"10009,10008,10007,10006","2022-11-19 14:10:20,2022-11-19 17:10:20,2022-11-19 19:10:10,2022-11-19 19:10:20"
10024,62284800003,2022-12-21 19:10:20,1,6,1671667820.0,"10009,10008,10007,10006","2022-11-19 14:10:20,2022-11-19 17:10:20,2022-11-19 19:10:10,2022-11-19 19:10:20"

本例读取的是这样格式的CSV 
最后两个列要解析到两个数据中,前面所有列解析到一个数组中
以逗号分隔

主体思路

 
先以逗号分隔到一个数组
然后从第1个双引号与第2个双引号是一个数组
第3个双引号与第4个双引号是一个数组 

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

//按逗号拆分一行文本为数组,row必须是数组
void ss_arr(char** res, char* row){
    char* tmp=NULL;
    char* val = strtok_r(row, ",", &tmp); 
    int i=0;
    while (val != NULL) {
        res[i] = val;
        val = strtok_r(NULL, ",",&tmp);
        i++;
    }
}

#define MAX_ARR_LEN 108
#define MAX_COL_NUM 7 

//最后两个字段列是数组
struct CsvRow
{
    char* arr[MAX_COL_NUM];
    char* arr2[MAX_ARR_LEN];
    char* arr3[MAX_ARR_LEN];
};

typedef struct CsvRow CRow;

void row_parser(CRow* cr, char* str){
    char* arr[MAX_ARR_LEN];
    int i;
    for(i=0;iarr[i]=NULL;   //除最后两个字段外的所有字段 
    }

    for(i=0;iarr2[i]=NULL;//id数组
        cr->arr3[i]=NULL;//时间数组 
    }

    ss_arr(arr,str);

    int arr_flag = 0;
    int n =0;
    int m =0;
    int h=0;
    int begin = 0;//数据部分标识,在CSV中排好的位置,两个数组相邻 
    for(i=0;arr[i]!=0;i++){
        int tmp_size = strlen(arr[i])-1;
        if (arr[i][0]=='\"' && arr[i][tmp_size] !='\"'){//最后两个数组中间首部
            begin=1;
            if(arr_flag==0){
                cr->arr2[n++] = &(arr[i])[1];
            }else if (arr_flag==1){
                cr->arr3[m++] = &(arr[i])[1];
            }
        }else if (arr[i][0]!='\"' && arr[i][tmp_size] =='\"' && begin>0){//最后两个数组中间尾部
            arr[i][tmp_size] = '\0';
            if(arr_flag==0){
                cr->arr2[n++] = arr[i];
            }else if (arr_flag==1){
                cr->arr3[m++] = arr[i];
            }
            arr_flag++;
        }else if (begin>0){//最后两个数组中间部分
            if(arr_flag==0){
                cr->arr2[n++] = arr[i];
            }else if (arr_flag==1){
                cr->arr3[m++] = arr[i];
            }
        }else{//除最后两个数组外的所有字段 
            cr->arr[h++] = arr[i];
        }
    }
}

int main(void) {
    char str[2000]="10024,62284800003,2022-12-21 19:10:20,1,6,1671667820.0,\"10009,10008,10007,10006\",\"2022-11-19 14:10:20,2022-11-19 17:10:20,2022-11-19 19:10:10,2022-11-19 19:10:20\"";
    
    CRow cr;
    row_parser(&cr,str);

    int j;
    for(j=0;cr.arr[j]!=0;j++){
        printf("arr[%d]=%s\n",j,cr.arr[j]);
    }

    for(j=0;cr.arr2[j]!=0;j++){
        printf("arr2[%d]=%s\n",j,cr.arr2[j]);
    }

    for(j=0;cr.arr3[j]!=0;j++){
        printf("arr3[%d]=%s\n",j,cr.arr3[j]);
    }
}

 
$ gcc c.c 
$ ./a.out 
arr[0]=10024
arr[1]=62284800003
arr[2]=2022-12-21 19:10:20
arr[3]=1
arr[4]=6
arr[5]=1671667820.0
arr2[0]=10009
arr2[1]=10008
arr2[2]=10007
arr2[3]=10006
arr3[0]=2022-11-19 14:10:20
arr3[1]=2022-11-19 17:10:20
arr3[2]=2022-11-19 19:10:10
arr3[3]=2022-11-19 19:10:20

补充说明

 
char* arr[MAX_COL_NUM];
字符串 指针数组,每个元素都是一个字符指针,指向一个 不定长 的字符串的首地址 
所以,无法通过malloc直接分配一个定长的地址

当然了,可以根据业务含义,取一个最大值,
比如这里的最大值就是时间的长度

C 日志

C 自定义写日志:超过MAX_LOG_SIZE被重置,带开关

 

#define MAX_LOG_SIZE 1048576 // 1M  
int log_open = 1;

//超过MAX_LOG_SIZE被重置
int lg(const char* LOG_FILE, bool isopen, char* line) {  
    // printf("open log:%d\n",isopen);
    if (!isopen){
        return -1;
    }

    FILE *fp;   
    size_t len;  
    long pos;  
    
    // 打开日志文件,如果文件不存在则创建  
    fp = fopen(LOG_FILE, "a+");  
    if (fp == NULL) {  
        printf("Failed to open log file.\n");  
        exit(1);  
    }  

    // 检查日志文件大小,如果超过1M则重写  
    fseek(fp, 0L, SEEK_END);  
    pos = ftell(fp);  
    if (pos > MAX_LOG_SIZE) {  
        rewind(fp); // 把文件指针移到文件开头  
        fp = freopen(LOG_FILE, "w", fp); // 重新打开文件,清空内容  
        printf("日志文件%s开始重写...\n",LOG_FILE);  
        if (fp == NULL) {  
            printf("Failed to reopen log file.\n");  
            exit(1);  
        }  
    }  
    
    // 写入日志  
    len = strlen(line)+50;
    char tmp[len] ;

    char currentTime[20];
    time_t rawtime;  
    struct tm * timeinfo;   
    
    time(&rawtime);  
    timeinfo = localtime(&rawtime);  
    strftime(currentTime, 20, "%Y-%m-%d %H:%M:%S", timeinfo);  
    
    sprintf(tmp,"[%s]:%s\n",currentTime,line); 
    fwrite(tmp, 1, strlen(tmp), fp);

    // 关闭文件  
    fclose(fp);  
    return 0;  
}    

其他文件引用时,可能会需要加上extern

 
extern int lg(const char* LOG_FILE, bool isopen, char* line);
extern int log_open;

C 自定义写日志:超过MAX_LOG_SIZE被重置

 
#include <stdio.h>
#include <stdlib.h>  
#include <string.h> 
#include <time.h>   
    
#define MAX_LOG_SIZE 1048576 // 1M  
    
//超过MAX_LOG_SIZE被重置
int lg(const char* LOG_FILE, char* line) {  
    FILE *fp;   
    size_t len;  
    long pos;  
    
    // 打开日志文件,如果文件不存在则创建  
    fp = fopen(LOG_FILE, "a+");  
    if (fp == NULL) {  
        printf("Failed to open log file.\n");  
        exit(1);  
    }  

    // 检查日志文件大小,如果超过1M则重写  
    fseek(fp, 0L, SEEK_END);  
    pos = ftell(fp);  
    if (pos > MAX_LOG_SIZE) {  
        rewind(fp); // 把文件指针移到文件开头  
        fp = freopen(LOG_FILE, "w", fp); // 重新打开文件,清空内容  
        printf("日志文件%s开始重写...\n",LOG_FILE);  
        if (fp == NULL) {  
            printf("Failed to reopen log file.\n");  
            exit(1);  
        }  
    }  
    
    // 写入日志  
    len = strlen(line)+50;
    char tmp[len] ;

    char currentTime[20];
    time_t rawtime;  
    struct tm * timeinfo;   
    
    time(&rawtime);  
    timeinfo = localtime(&rawtime);  
    strftime(currentTime, 20, "%Y-%m-%d %H:%M:%S", timeinfo);  
    
    sprintf(tmp,"[%s]:%s\n",currentTime,line); 
    fwrite(tmp, 1, strlen(tmp), fp);

    // 关闭文件  
    fclose(fp);  
    return 0;  
}

int main() {  
    char line[1024] = "a一\n"; 
    lg("log.txt",line);

    char* line2 = "b三";
    lg("log.txt",line2);
    int i ;
    for(i=0;i < 1000000;i++){
        lg("log.txt","测试是否会重置-----------------------------");
    }
    return 0;  
}

C invalid elf header

 
如果将一个文件按字节转int,再从int转字节写入文件,可以吗?
对于txt完全没问题
对于部分gcc编辑出来的可执行文件也没问题


ELF(Executable and Linking Format)是一种对象文件的格式, 在linux平台上被广泛接受,作为缺省的二进制文件格式来使用。

$ file librust2py.so 
librust2py.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=85b35abbcf194ab73a8fb360cade3eb14ce56623, not stripped

对于.so库文件有问题,目的在于防篡改... 

C fseek

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

 
从指定位置origin移到文件指针offset个字节 

origin有三种,分别为:
SEEK_CUR - 文件指针当前的位置
SEEK_END - 文件末尾的位置,0或负数
SEEK_SET - 文件开始的位置,0或正数 

C read

ssize_t read( int fd, void *buf, size_t nbyte) :将数据从外设上经过内核读到用户空间

 
size_t nbyte (size_t为无符号整型)
一次从buf中读取nbyte个字节到描述符为fd的文件中,
对于大文件来说,该值设置大一点可以减少与内核交互次数,进而提高整体速度


返回值 (ssize_t为有符号整型)
大于0,读取字节数,
等于0,表示读到文件结束.
小于0,读取失败. EINTR-中断, ECONNREST-网络问题. 

c stat

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

int main(){

    // stat结构体
    struct stat fil_info;

    // stat函数
    stat("/tmp",&fil_info);

    //0-成功,-1-失败
    if((stat("/tmp",&fil_info))==0){

        //mode_t st_mode,文件类型及存储权限
        printf("st_mode:%d\n",fil_info.st_mode);  //st_mode:17407

        //ino_t st_ino,文件的序列号
        printf("st_ino:%ld\n",fil_info.st_ino);  //st_ino:2359297

        //off_t st_size,byte 
        printf("st_size:%ld\n",fil_info.st_size);//st_size:4096

        //time_t st_mtime,最后被修改时间
        printf("st_mtime:%ld\n",fil_info.st_mtime); //st_mtime:1689228870

        //time_t st_ctime,最后状态改变时间 
        printf("st_ctime:%ld\n",fil_info.st_ctime); //st_ctime:1689228870

    }
    
    return 0;
}


参考
    C语言 读取文件内容
    文件描述符(通俗易懂)
    C语言文件操作 

    read和write函数的使用 

    C语言中write函数