在有些产品的研发过程中,一般我们都有很多条数据记录在一个LOG文件中。
在查看最新的数据记录都是从最开始保存的那条开始存储,所以,参考了网上一些实现方法,稍微修改了一下即得到了下面的解决方法:
假设文件存储如下内容:
2018/10/24 16:28 | 橙子 | 水果 |
2018/10/21 10:16 | 啤酒 | 饮料 |
2018/10/22 10:16 | 水杯 | 玻璃瓶 |
2018/10/23 09:19 | 硫酸 | 腐蚀品 |
2018/10/24 10:17 | 火药 | 爆炸物 |
2018/10/25 09:19 | 火柴 | 易燃物 |
2018/10/26 09:29 | 可乐 | 饮料 |
2018/10/27 10:39 | 雪碧 | 饮料 |
2018/10/28 09:49 | 益力多 | 饮料 |
2018/10/29 09:59 | 优益C | 饮料 |
由于.csv格式的文件是如下的格式,所以我们知道它是以逗号作为分隔符的,一行的结束符就是\n
2018/10/24 16:28,橙子,水果
2018/10/21 10:16,啤酒,饮料
2018/10/22 10:16,水杯,玻璃瓶
2018/10/23 09:19,硫酸,腐蚀品
2018/10/24 10:17,火药,爆炸物
2018/10/25 09:19,火柴,易燃物
2018/10/26 09:29,可乐,饮料
2018/10/27 10:39,雪碧,饮料
2018/10/28 09:49,益力多,饮料
2018/10/29 09:59,优益C,饮料
运行结果:
上面的这种情形可以说在数据量小的情况下可以这么来实现,但数据量一大我们就需要选择更适合的数据结构了,可以用链表来解决这样的问题:
typedef struct links
{
int size ;
void *ptr ;
struct links *next ;
struct links *pre ;
}LINKS;
这是链表的数据结构,ptr就是要存放的数据,pre是前驱指针,next是后继指针,通过这两个指针,即可以方便实现链表的遍历。
下面定义要实现的函数:
typedef void (*print_t)(void *Data) ;
void print(void *Data);
//打印链表节点
void print_links(LINKS *Header , print_t func);
//创建链表头
LINKS *Create_Links_Header(int size);
//头插
void top_append(LINKS *Header , void *Data , int size);
//获取文件总行数
int GetTotalLineCount(FILE *file);
整体实现:
#include
#include
#include
#define NR(x) (sizeof(x)/sizeof(x[0]+0))
typedef struct links
{
int size ;
void *ptr ;
struct links *next ;
struct links *pre ;
}LINKS;
typedef void (*print_t)(void *Data) ;
void print(void *Data);
void print_links(LINKS *Header , print_t func);
LINKS *Create_Links_Header(int size);
void top_append(LINKS *Header , void *Data , int size);
int GetTotalLineCount(FILE *file);
int main(void)
{
int line = 0 ;
int file_all_line = 0 ;
char line_buffer[50] = {0};
LINKS *Header = NULL ;
//1.初始化链表
Header = Create_Links_Header(0);
if(NULL == Header)
{
fprintf(stderr , "malloc Header fail \n");
return -1 ;
}
//2.插入数据
FILE *fp = NULL ;
fp = fopen("1.csv","r");
if(NULL == fp)
{
printf("open csv file fail!\n");
return -1 ;
}
//移到文件头
fseek(fp,0,SEEK_SET);
//获取文件的总行数
file_all_line = GetTotalLineCount(fp);
for(line = 0 ; line < file_all_line ; line++)
{
if(fgets(line_buffer,50,fp))
{
printf("line_buffer:%s",line_buffer);
top_append(Header , line_buffer , 100);
}
}
print_links(Header,print);
fclose(fp);
free(Header);
return 0 ;
}
void print(void *Data)
{
printf("%s" ,Data);
//这里可以进行数据处理...
//这里可以进行数据处理...
}
//打印链表节点
void print_links(LINKS *Header , print_t func)
{
LINKS *tmp = Header->next ;
while(tmp != Header)
{
func(tmp->ptr);
tmp = tmp->next ;
free(tmp->pre);
}
}
//获取文件的总行数
int GetTotalLineCount(FILE *file)
{
int line_num = 0;
char strLine[50];
fseek(file,0,SEEK_SET);
while (fgets(strLine, 50, file))
line_num++;
fseek(file,0,SEEK_SET);
return line_num;
}
//创建表头
LINKS *Create_Links_Header(int size)
{
LINKS *New = NULL ;
New = malloc(sizeof(LINKS));
if(NULL == New)
{
fprintf(stderr , "malloc LINKS header fail \n");
return NULL ;
}
New->size = size ;
New->ptr = NULL ;
New->next = New ;
New->pre = New ;
return New ;
}
//链表头插
void top_append(LINKS *Header , void *Data , int size)
{
LINKS *New = NULL ;
New = malloc(sizeof(LINKS));
if(NULL == New)
{
fprintf(stderr , "malloc links fail \n");
return ;
}
New->ptr=NULL ;
New->size = size ;
New->ptr = malloc(size);
if(NULL == New->ptr)
{
fprintf(stderr , "malloc links data fail \n");
return ;
}
memcpy(New->ptr , Data , size);
New->next = Header->next ;
New->pre = Header ;
New->next->pre = New ;
New->pre->next = New ;
}
运行结果:
如下图所示为excel文件的数据:
运行程序得到:
从这里看到,整个程序就是利用了栈的思想,先进后出,这样的实现既简单,也高效。我们再来看一个案例---C语言读取CSV文件任意一行并拆分该行获取数据:
void Display_Record_Data(int line)
当line等于0时,表示读取CSV文件的第0行数据,等于100时,表示读取CSV文件的第100行数据,依次类推。
以下是我们要获取的数据,数据格式为:序号,时间,阈值,气雾,灵敏度
我们要做的事情就是,当传入为1时,则获取为当前CSV文件的第一行数据,并拆分出来。
源码实现:
#include
#include
#include
#include
#define SERIAL_NUMBER 0
#define DATE_TIME 1
#define DETECT_VALUE 2
#define SENSOR_STATU 3
#define SENSIVITY_STATU 4
struct Record_Page_Data
{
int serial_number ;
char date_time[20] ;
int detect_result ;
int sensor_status ;
int sensivity_status ;
int year ;
int month ;
int day ;
int hour ;
int minute ;
char _year[10] ;
char _month[10] ;
char _day[10] ;
char _hour[10] ;
char _minute[10] ;
};
struct Record_Page_Data csv_file_record ;
//根据逗号拆分字符串
static int splitStr(char *str,char separator, char strs[][40])
{
int i = 0,j = 0,n = 0;
for(i = 0; str[i]; ++i) {
if(str[i] == separator) {
strs[n++][j] = 0;
j = 0;
}
else strs[n][j++] = str[i];
}
strs[n++][j] = 0;
return n;
}
//显示记录数据
void Display_Record_Data(int line)
{
FILE *fp;
int para = 0 ;
int item = 0 ;
int which_line = line; //指定要读取哪一行
if(which_line == 0 || which_line < 0)
return ;
if(which_line > 0)
which_line-= 1 ;
int current_index=0; //当前读取的行
char strs[5][40] = {0};
char strLine[50]; //每行最大读取的字符数,可根据实际情况扩大
if((fp = fopen("file.csv","r")) == NULL) //判断文件是否存在及可读
{
printf("error!");
return ;
}
while (!feof(fp))
{
if(current_index == which_line)
{
fgets(strLine,50,fp); //读取一行
printf("CSV文件原始数据:%s", strLine); //输出
item = splitStr(strLine,',',strs);
for(para = 0 ; para < item ; para++)
{
switch(para)
{
case SERIAL_NUMBER:
csv_file_record.serial_number = atoi(strs[para]);
break ;
case DATE_TIME :
memcpy(csv_file_record.date_time,strs[para],strlen(strs[para]));
memcpy(csv_file_record._year,csv_file_record.date_time,4);
memcpy(csv_file_record._month,csv_file_record.date_time+5,2);
memcpy(csv_file_record._day,csv_file_record.date_time+8,2);
memcpy(csv_file_record._hour,csv_file_record.date_time+11,2);
memcpy(csv_file_record._minute,csv_file_record.date_time+14,2);
csv_file_record.year = atoi(csv_file_record._year);
csv_file_record.month = atoi(csv_file_record._month);
csv_file_record.day = atoi(csv_file_record._day);
csv_file_record.hour = atoi(csv_file_record._hour);
csv_file_record.minute = atoi(csv_file_record._minute);
break ;
case DETECT_VALUE :
csv_file_record.detect_result = atoi(strs[para]);
break ;
case SENSOR_STATU :
csv_file_record.sensor_status = atoi(strs[para]);
break ;
case SENSIVITY_STATU:
csv_file_record.sensivity_status = atoi(strs[para]);
break ;
}
}
}
fgets(strLine,50,fp); //读取一行,并定位到下一行
current_index++;
}
printf("序号:%d 时间:%d年%d月%d日 %d时%d分 阈值显示:%d 气雾状态:%d%% 灵敏度状态:%d\n",csv_file_record.serial_number,csv_file_record.year,csv_file_record.month, \
csv_file_record.day,csv_file_record.hour,csv_file_record.minute,csv_file_record.detect_result,csv_file_record.sensor_status,csv_file_record.sensivity_status);
fclose(fp); //关闭文件
return ;
}
int main(void)
{
int i ;
for(i = 1 ; i < 8 ; i++)
Display_Record_Data(i);
return 0 ;
}
运行结果:
在单片机fatfs上,我们一样也可以实现这样的功能,根据不同的实现可以组合出多种多样的应用。
参考资料:
https://blog.csdn.net/moqj_123/article/details/45897877