0x00 脚下的路
不知道为啥要写这个小标题,可能是年轻的心想体验一下苍老的感觉,抑或是少年的一阵迷茫。混沌的四年,终究还是入了这一行。从初时的不知,到现在的刚开始,中间的间隔竟是四年之久,想起了陈奕迅的《十年》,但却不像医生所唱的十年那么有故事。或许这四年有这四年的价值,这四年也应有所积累。少年狂应少发,更不是什么老夫,当然也没有什么资格。之后的路应该在脚下,应是一步一步。比较喜欢的一句话,不去想目的地,才能轻松踏上旅途。少些心血来潮,多些精专。
聊以此记,舒心之惑,明己之志。
0x01 歌词解析
这是一个用c语言完成的小项目,大神们称之为玩具。不过对于对于知识的积累确有不小的作用。对于自己也是很好的训练,吃透每个小项目,吃透每一个细节,故总结之。
何为歌词解析?效果如何。刚开始必然是一片迷茫,可使用过各种播放器的我们应该对此熟悉。这是平日的歌曲播放器的部分截图:
管中窥豹,实现后的具体效果大致也应如此。当然,歌词解析吗?更应该了解歌词是什么样子的,有什么样子的格式(我们都明白,一切的神秘下面都隐藏着简单的道理)。这是某个词文件的部分截图:
如此,大致便可有些许想法了:将歌词文件读取出来,逐行显示在屏幕上,并随着歌曲播放时间逐行滚动。
0x02 流程分析
通过观察歌词,可以想到使用链表存储歌词信息比较方便,因为歌词要一行一行显示,并且事先并不了解每首歌的歌词有多少句。可以将歌词的每一句存入链表的一个节点。
整个流程大致可以分为如下几块:
- 读取歌词文件
- 按行切割歌词文件
- 逐行分析歌词头部歌曲信息部分
- 逐行分析歌词正文部分,并根据每行歌词前的时间将歌词插入链表
- 模拟时钟,根据时间显示歌词和播放歌曲
- 歌词滚屏,歌词反显(当前歌词突出显示)
0x03 读取歌词文件
文本文件的读取大致分如下步骤:
- 打开歌词文件
- 计算歌词文件的大小
- 分配内存空间
- 将歌词读入内存
- 关闭歌词文件
/*读取歌词*/
void read_lrc(char *p_lrc, char **lrc_data)
{
/*打开歌词文件*/
FILE *fp = fopen(p_lrc, "r");
if(fp == NULL)
{
perror("fopen");
return;
}
/*计算歌词文件的大小*/
fseek(fp, 0, 2);
long lrc_len = ftell(fp);
rewind(fp);
/*分配内存空间*/
*lrc_data = (char *)calloc(1, lrc_len);
if(*lrc_data == NULL)
{
perror("ralloc");
return;
}
/*将歌词读入内存*/
fread(*lrc_data, lrc_len, 1, fp);
/*关闭文件*/
fclose(fp);
return;
}
0x04 按行切割歌词文件
歌词切割应注意如下(百度百科):
/*将歌词逐行分割*/
void lrctok(char ** buf, char *lrc_data)
{
buf[0] = lrc_data;
int i = 0;
while((buf[i] = (char *)strtok(buf[i], "\r\n")))
i++;
return;
}
0x05 逐行分析歌词头部
歌词头部信息格式(百度百科):
由于头部信息比较简单,所以并不把头部信息存入链表,而是直接分析后显示:
/*分析歌词头部信息*/
int analysis_lrc_head(char **buf)
{
clear_screen();
cusor_moveto(0, 0);
set_fg_color(COLOR_GREEN);
/*逐行分析头部*/
int i = 0;
while(*(buf[i] + 1) > '0')/*解释歌词信息*/
{
/*读取头部信息内容*/
char lrc[128]="";
sscanf(buf[i],"%*[^:]:%[^]]", lrc);
/*读取头部信息标签*/
char tags[10] = "";
sscanf(buf[i], "[%[^:]", tags);
cusor_moveto(35, i+1);
if(strcmp(tags, "ti") == 0)
{
printf("歌名:");
}
else if(strcmp(tags, "ar") == 0)
{
printf("歌手:");
}
else if(strcmp(tags, "al") == 0)
{
printf("专辑:");
}
else if(strcmp(tags, "by") == 0)
{
printf("制作:");
}
else if(strcmp(tags, "offset") == 0)
{
printf("offset:");
}
printf("%s\n", lrc);
i++;
}
//printf("%d\n", i);
return i;
}
0x06 逐行分析歌词正文
对于歌词正文部分,要处理的有两个方面:一方面分析每句歌词前面的时间,另一方面,将根据前面的时间将歌词插入链表。
/*分析歌词正文部分*/
int analysis_lrc_body(char **buf,int star_num, List *list)
{
int i = star_num;/*从头部信息之后开始分析*/
while(buf[i])/*解析歌词正文*/
{
char *str_lrc = buf[i];
while(*str_lrc == '[')/*寻找歌词位置*/
str_lrc +=10;
//printf("%s\n", str_lrc);/*打印当前歌词测试*/
char *str_time = buf[i];/*解析每句歌词前的时间*/
while(*str_time == '[')
{
int m = 0,s = 0;
sscanf(str_time,"[%d:%d.%*d]", &m, &s);
int time = m*60+s;//以秒为单位
/*将时间和歌词 --对应 放入结构体*/
LRC *tmp = (LRC *)calloc(1, sizeof(LRC));
if(tmp == NULL)
{
perror("calloc");
return i;
}
tmp->time = time;
strcpy(tmp->lrc, str_lrc);
//调用链表的有序插入函数
list_ins_sort(list, tmp);
//分析下一个时间
str_time += 10;
}
i++;
}
return i;
}
0x07 模拟时钟
使用模拟时钟的目的是模拟实现歌曲播放与歌词同步,模拟时钟和歌曲播放器同时启动,然后根据模拟时钟的时间在歌词链表中查找。
int i = 0;/*模拟计时器*/
while(1)
{
/*根据时间i到链表中查找,并且打印歌词*/
time_delay(1);
i++;
}
0x08 歌词滚屏、歌词反选
/*模拟时钟*/
int i = 0;/*模拟计时器*/
char show[5][128] = {"","","","",""};
while(1)
{
/*显示进度条*/
int base = 15;/*进度条从第base列起始*/
set_fg_color(COLOR_RED);
cusor_moveto(base, line + 9);
printf("%02d:%02d|",i/60,i%60);/*以mm:ss格式打印时间*/
fflush(stdout);
int endtime = ((LRC *)(list.tail->data))->time;
int length = (i*1.0)/endtime*45;
cusor_moveto(base + 6, line + 9);
int j = 0;
for(; j < length; j++)
{
printf(">");
}
cusor_moveto(base + 51, line + 9);
printf("|%02d:%02d",endtime/60, endtime%60);
/*查找此时的歌词*/
int offset = 0;/*调整歌词与歌曲同步偏差*/
LRC tmp;/*按时间查找歌词*/
tmp.time = i + offset;/*初始化查找时间*/
strcpy(tmp.lrc, "");
Node *ret = list_search(&list, &tmp);/*返回查找歌词在链表中的位置*/
if(ret != NULL)
{
/*滚动显示歌词*/
strcpy(show[0], show[1]);
strcpy(show[1], show[2]);
strcpy(show[2], show[3]);
strcpy(show[3], show[4]);
strcpy(show[4], ((LRC *)(ret->data))->lrc);
cusor_moveto(30, line+2);
set_fg_color(COLOR_WHITE);
printf("%s \n", show[0]);
fflush(stdout);
cusor_moveto(30, line+3);
printf("%s \n", show[1]);
fflush(stdout);
cusor_moveto(30, line+4);
printf("%s \n", show[2]);
fflush(stdout);
cusor_moveto(30, line+5);
printf("%s \n", show[3]);
fflush(stdout);
cusor_moveto(30, line+6);
set_fg_color(COLOR_BLUE);
printf("%s \n", show[4]);
set_fg_color(COLOR_WHITE);
fflush(stdout);
if(ret->next == NULL)
{
cusor_moveto(0, line+25);
cusor_show();
break;
}
}
time_delay(1);
i++;
}
0x09 运行结果
0x0a 全部程序
[my_lrc]tree
.
├── list
│ ├── list.c
│ └── list.h
├── lrc
│ ├── coffe.lrc
│ ├── 邓紫棋-存在.lrc
│ ├── 邓紫棋-喜欢你.lrc
│ └── 简单爱.lrc
├── lrc.c
├── lrc.h
├── main
├── main.c
├── makefile
├── mplayer
│ ├── start_mplayer.c
│ └── start_mplayer.h
├── pos
│ ├── console.c
│ └── console.h
├── song
│ ├── coffe.mp3
│ ├── 邓紫棋-存在.mp3
│ ├── 邓紫棋-喜欢你.mp3
│ └── 简单爱.mp3
└── time_delay
├── time_delay.c
└── time_delay.h
makefile
CC=gcc
obj=main.o lrc.o list.o start_mplayer.o console.o time_delay.o list.o
target=main
CFLAGS=-Wall
$(target):$(obj)
@$(CC) $^ -o $@ $(CFLAGS)
main.o:main.c
@$(CC) -c $< -o $@ $(CFLAGS)
lrc.o:lrc.c
@$(CC) -c $< -o $@ $(CFLAGS)
start_mplayer.o:./mplayer/start_mplayer.c
@$(CC) -c $< -o $@ $(CFLAGS)
console.o:./pos/console.c
@$(CC) -c $< -o $@ $(CFLAGS)
time_delay.o:./time_delay/time_delay.c
@$(CC) -c $< -o $@ $(CFLAGS)
list.o:./list/list.c
@$(CC) -c $< -o $@ $(CFLAGS)
clean:
@rm $(obj) -rf
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "time_delay/time_delay.h"
#include "mplayer/start_mplayer.h"
#include "list/list.h"
#include "pos/console.h"
#include "lrc.h"
int main(int argc, char *argv[])
{
/*读取歌词文件*/
char *lrc_data = NULL;
read_lrc("./lrc/coffe.lrc", &lrc_data);
/*将歌词按行分割*/
char *buf[128]={NULL};
lrctok(buf, lrc_data);
/*逐行解析歌词*/
List list;/*创建歌词链表*/
list_init(&list, destroy, print, time_cmp);
cusor_hide();
int line = analysis_lrc_head(buf);/*分析歌词头部信息*/
analysis_lrc_body(buf, line, &list);/*分析歌词正文*/
//print_list(&list);/*打印歌词链表调试*/
/*启动maplayer*/
mplayer_play("./song/coffe.mp3");
/*模拟时钟*/
int i = 0;/*模拟计时器*/
char show[5][128] = {"","","","",""};
while(1)
{
/*显示进度条*/
int base = 15;/*进度条从第base列起始*/
set_fg_color(COLOR_RED);
cusor_moveto(base, line + 9);
printf("%02d:%02d|",i/60,i%60);/*以mm:ss格式打印时间*/
fflush(stdout);
int endtime = ((LRC *)(list.tail->data))->time;
int length = (i*1.0)/endtime*45;
cusor_moveto(base + 6, line + 9);
int j = 0;
for(; j < length; j++)
{
printf(">");
}
cusor_moveto(base + 51, line + 9);
printf("|%02d:%02d",endtime/60, endtime%60);
/*查找此时的歌词*/
int offset = 0;/*调整歌词与歌曲同步偏差*/
LRC tmp;/*按时间查找歌词*/
tmp.time = i + offset;/*初始化查找时间*/
strcpy(tmp.lrc, "");
Node *ret = list_search(&list, &tmp);/*返回查找歌词在链表中的位置*/
if(ret != NULL)
{
/*滚动显示歌词*/
strcpy(show[0], show[1]);
strcpy(show[1], show[2]);
strcpy(show[2], show[3]);
strcpy(show[3], show[4]);
strcpy(show[4], ((LRC *)(ret->data))->lrc);
cusor_moveto(30, line+2);
set_fg_color(COLOR_WHITE);
printf("%s \n", show[0]);
fflush(stdout);
cusor_moveto(30, line+3);
printf("%s \n", show[1]);
fflush(stdout);
cusor_moveto(30, line+4);
printf("%s \n", show[2]);
fflush(stdout);
cusor_moveto(30, line+5);
printf("%s \n", show[3]);
fflush(stdout);
cusor_moveto(30, line+6);
set_fg_color(COLOR_BLUE);
printf("%s \n", show[4]);
set_fg_color(COLOR_WHITE);
fflush(stdout);
if(ret->next == NULL)
{
cusor_moveto(0, line+25);
cusor_show();
break;
}
}
time_delay(1);
i++;
}
/*释放空间*/
if(lrc_data != NULL)
{
free(lrc_data);
lrc_data = NULL;
}
list_destroy(&list);
return 0;
}
lrc.h
#ifndef __LRC_H__
#define __LRC_H__
#include "list/list.h"
typedef struct lrc
{
//数据域
int time;
char lrc[128];
}LRC;
extern void print(const void *data);/*打印歌词结构体*/
extern int time_cmp(const void *key1, const void *key2);/*歌词结构的比较,按时间比较*/
extern void destroy(void *data);/*歌词结构体空间的释放*/
extern void read_lrc(char *p_lrc, char **lrc_data);/*读取歌词文件,将内容写入制定内存空间,函数外部分配空间*/
extern void lrctok(char ** buf, char *lrc_data);/*逐行分割歌词*/
extern int analysis_lrc_head(char **buf);/*分析歌词头部信息*/
extern int analysis_lrc_body(char **buf,int star_num, List *list);/*分析歌词正文信息*/
#endif //__LRC_H__
lrc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lrc.h"
#include "list/list.h"
#include "pos/console.h"
/*定义歌词结构的打印方式*/
void print(const void *data)
{
LRC *lrc_data = (LRC *)data;
printf("%s\n", lrc_data->lrc);
return;
}
/*定义歌词结构的比较方式,此处以时间比较*/
int time_cmp(const void *key1, const void *key2)
{
LRC *tm1 = (LRC *)key1;
LRC *tm2 = (LRC *)key2;
if(tm1->time > tm2->time)
return 1;
else if(tm1->time < tm2->time)
return -1;
else
return 0;
}
/*定义歌词结构的空间的释放方式*/
void destroy(void *data)
{
if(data != NULL)
{
free(data);
data = NULL;
}
return;
}
/*读取歌词*/
void read_lrc(char *p_lrc, char **lrc_data)
{
/*打开歌词文件*/
FILE *fp = fopen(p_lrc, "r");
if(fp == NULL)
{
perror("fopen");
return;
}
/*计算歌词文件的大小*/
fseek(fp, 0, 2);
long lrc_len = ftell(fp);
rewind(fp);
/*分配内存空间*/
*lrc_data = (char *)calloc(1, lrc_len);
if(*lrc_data == NULL)
{
perror("ralloc");
return;
}
/*将歌词读入内存*/
fread(*lrc_data, lrc_len, 1, fp);
/*关闭文件*/
fclose(fp);
return;
}
/*将歌词逐行分割*/
void lrctok(char ** buf, char *lrc_data)
{
buf[0] = lrc_data;
int i = 0;
while((buf[i] = (char *)strtok(buf[i], "\r\n")))
i++;
return;
}
/*分析歌词头部信息*/
int analysis_lrc_head(char **buf)
{
clear_screen();
cusor_moveto(0, 0);
set_fg_color(COLOR_GREEN);
/*逐行分析头部*/
int i = 0;
while(*(buf[i] + 1) > '0')/*解释歌词信息*/
{
/*读取头部信息内容*/
char lrc[128]="";
sscanf(buf[i],"%*[^:]:%[^]]", lrc);
/*读取头部信息标签*/
char tags[10] = "";
sscanf(buf[i], "[%[^:]", tags);
cusor_moveto(35, i+1);
if(strcmp(tags, "ti") == 0)
{
printf("歌名:");
}
else if(strcmp(tags, "ar") == 0)
{
printf("歌手:");
}
else if(strcmp(tags, "al") == 0)
{
printf("专辑:");
}
else if(strcmp(tags, "by") == 0)
{
printf("制作:");
}
else if(strcmp(tags, "offset") == 0)
{
printf("offset:");
}
printf("%s\n", lrc);
i++;
}
//printf("%d\n", i);
return i;
}
/*分析歌词正文部分*/
int analysis_lrc_body(char **buf,int star_num, List *list)
{
int i = star_num;/*从头部信息之后开始分析*/
while(buf[i])/*解析歌词正文*/
{
char *str_lrc = buf[i];
while(*str_lrc == '[')/*寻找歌词位置*/
str_lrc +=10;
//printf("%s\n", str_lrc);/*打印当前歌词测试*/
char *str_time = buf[i];/*解析每句歌词前的时间*/
while(*str_time == '[')
{
int m = 0,s = 0;
sscanf(str_time,"[%d:%d.%*d]", &m, &s);
int time = m*60+s;//以秒为单位
/*将时间和歌词 --对应 放入结构体*/
LRC *tmp = (LRC *)calloc(1, sizeof(LRC));
if(tmp == NULL)
{
perror("calloc");
return i;
}
tmp->time = time;
strcpy(tmp->lrc, str_lrc);
//调用链表的有序插入函数
list_ins_sort(list, tmp);
//分析下一个时间
str_time += 10;
}
i++;
}
return i;
}
mplayer_play.h
#ifndef __START_MPLAYER_H__
#define __START_MPLAYER_H__
//启动mplayer播放器
//参数song_path 为歌曲的路径
extern void mplayer_play(char * song_path);
#endif
mplayer_play.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
//启动mplayer播放器
//参数song_path 为歌曲的路径
void mplayer_play(char * song_path)
{
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork");
}
else if(pid==0)
{
close(1);
close(2);
execlp("mplayer","mplayer","-slave","-quiet",song_path,NULL);
exit(0);
}
else
;
}
console.h
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
#define COLOR_RED 31
#define COLOR_BLACK 30
#define COLOR_GREEN 32
#define COLOR_BLUE 34
#define COLOR_YELLOW 33
#define COLOR_WHITE 37
#define COLOR_CYAN 36
#define COLOR_MAGENTA 35
/*
COLOR_RED 红
COLOR_BLACK 黑
COLOR_GREEN 绿
COLOR_BLUE 蓝
COLOR_YELLOW 黄
COLOR_WHITE 白
COLOR_CYAN 青
COLOR_MAGENTA 洋红
*/
extern void cusor_moveto(int x, int y);//光标跳转到 y行 x列
extern void cusor_get_pos(void);//保存光标位置
extern void cusor_hide(void);//隐藏光标
extern void cusor_show(void);//显示光标
extern void cusor_set_pos(void);//恢复光标位置
extern void clear_screen(void);//清屏
extern void set_fg_color(int color);//设置字体前景色
extern void set_bg_color(int color);//设置字体背景色
#endif //_CONSOLE_H_
console.c
#include <stdio.h>
#include <stdlib.h>
#include "console.h"
void cusor_moveto(int x, int y)
{// ESC[y;xH
printf("\033[%d;%dH",y,x);
fflush(stdout);
}
//保存光标位置
void cusor_get_pos(void)
{// ESC[s
printf("\033[s");
fflush(stdout);
}
//恢复光标位置
void cusor_set_pos(void)
{// ESC[u
printf("\033[u");
fflush(stdout);
}
//隐藏光标
void cusor_hide(void)
{
printf("\033[?25l");
}
//显示光标
void cusor_show(void)
{
printf("\33[?25h");
}
//清屏
void clear_screen(void)
{// ESC[2J
printf("\033[2J");
fflush(stdout);
}
/*
COLOR_RED 红
COLOR_BLACK 黑
COLOR_GREEN 绿
COLOR_BLUE 蓝
COLOR_YELLOW 黄
COLOR_WHITE 白
COLOR_CYAN 青
COLOR_MAGENTA 洋红
*/
//设置前景颜色
void set_fg_color(int color)
{// ESC[#m
printf("\033[%dm",color);
fflush(stdout);
}
//设置背景颜色
void set_bg_color(int color)
{// ESC[#m
printf("\033[%dm",(color+10));
fflush(stdout);
}
time_delay.h
#ifndef __TIME_DELAY__
#define __TIME_DELAY__
extern void time_delay(int sec);
#endif //__TIME_DELAY__
time_delay.c
#include <stdio.h>
#include <time.h>
void time_delay(int sec)
{
int s_time,e_time;
s_time=time(NULL);
while(1)
{
e_time=time(NULL);
if(e_time==s_time+sec)
break;
}
}
list.h
#ifndef LINK_H
#define LINK_H
typedef struct node//链表元素的结构
{
void *data;/*节点中的数据域,设置为无类型指针,数据类型大小由使用者定义*/
struct node *next;/*指向下一节点的指针*/
}Node;
typedef struct list/*链表的结构*/
{
int size;/*链表中节点个数*/
void (*destroy)(void *data);/*由于链表节点中的数据是用户自定义的,故需要调用者提供释放空间的函数*/
void (*print_data)(const void *data);/*同,由用户自定义打印数据的函数*/
int (*match)(const void *key1, const void *key2);/*同,由用户自定义数据的比较方式*/
Node *head;/*记录链表的头部位置*/
Node *tail;/*记录链表的尾部位置*/
}List;
extern void list_init(List *list, void (*destroy)(void *data), void (*print_data)(const void *data), \
int (*match)(const void *key1, const void *key2));/*初始化一个链表*/
extern int list_ins_head(List *list, const void *data);/*链表的插入,将节点从头部插入*/
extern int list_ins_tail(List *list, const void *data);/*链表的插入,将节点从尾部插入*/
extern int list_ins_sort(List *list, const void *data);/*链表的插入,插入后链表是一个有序的链表*/
extern void* list_search(List *list, const void *data);/*在链表中查找指定数据,若找到返回链表中的位置*/
extern void* list_remove(List *list, const void *data);/*在链表中删除指定数据,若找到删除节点并将数据地址返回*/
extern void list_reverse(List *list);//将链表逆置*/
extern void list_sort(List *list);/*将链表按照一定方式排序*/
extern void print_list(List *list);/*打印链表*/
extern void list_destroy(List *list);/*删除整个链表*/
#define list_size(list) (list->size) /*返回链表节点个数*/
#endif // LINK_H
list.c
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*初始化链表*/
void list_init(List *list, void (*destroy)(void *data), void (*print_data)(const void *data), \
int (*match)(const void *key1, const void *key2))
{
list->size = 0;
list->head = NULL;
list->tail = NULL;
list->match = match;
list->destroy = destroy;
list->print_data = print_data;
return;
}
/*打印整个链表*/
void print_list(List *list)
{
if(list->head == NULL)//链表为空
{
printf("list is empty\n");
}
else //链表非空
{
Node * p_cur = list->head;
while (p_cur)/*遍历链表*/
{
list->print_data(p_cur->data);
p_cur = p_cur->next;
}
}
return;
}
/*在链表的头部插入数据*/
int list_ins_head(List *list, const void *data)
{
Node *new_node = (Node *)calloc(1, sizeof (Node)); //创建插入的节点
if(new_node == NULL)
return -1;
new_node->data = (void *)data;//关联节点与数据
/*
if(list_size(list) == 0)//链表为空时,插入节点
{
list->tail = new_node;
new_node->next = NULL;
list->head = new_node;
}
else //链表非空时将节点插入头部
{
new_node->next = list->head;
list->head = new_node;
}
*/
if(list_size(list) == 0)//链表为空时,插入节点
list->tail = new_node;
new_node->next = list->head;
list->head = new_node;
list->size ++;
return 0;
}
/*在链表的尾部插入数据*/
int list_ins_tail(List *list, const void *data)
{
Node *new_node = (Node *)calloc(1, sizeof (Node)); //创建插入的节点
if(new_node == NULL)
return -1;
new_node->data = (void *)data;//关联节点与数据
if(list_size(list) == 0)
list->head = new_node;
else
list->tail->next = new_node;
list->tail = new_node;
new_node->next = NULL;
list->size ++;
return 0;
}
/*在链表的有序插入数据*/
int list_ins_sort(List *list, const void *data)
{
Node *new_node = (Node *)calloc(1, sizeof (Node)); //创建插入的节点
if(new_node == NULL)
return -1;
new_node->data = (void *)data;//关联节点与数据
if(list_size(list) == 0)//链表为空时,插入节点
{
list->tail = new_node;
new_node->next = NULL;
list->head = new_node;
}
else//链表非空时
{
Node *p_cur = list->head;
Node *p_pre = list->head;
while(p_cur != NULL && list->match(new_node->data, p_cur->data) > 0)//查找链表的插入位置
{
p_pre = p_cur;
p_cur = p_cur->next;
}
if(p_cur != NULL)//插入位置在头部和中间时
{
if(p_cur == list->head)//插入位置在头部
{
new_node->next = list->head;
list->head = new_node;
}
else//位置在链表中间
{
new_node->next = p_pre->next;
p_pre->next = new_node;
}
}
else//插入位置在链表尾部
{
list->tail->next = new_node;
list->tail = new_node;
new_node = NULL;
}
}
list->size ++;
return 0;
}
/*查找链表中与数据匹配的节点,并返回节点指针*/
void* list_search(List *list, const void *data)
{
if(list_size(list) == 0)
{
printf("list is empty\n");
return NULL;
}
else
{
Node *p_cur = list->head;
while(p_cur != NULL && list->match(p_cur->data, data) != 0)//查找数据在链表中的位置
{
p_cur = p_cur->next;
}
return p_cur;
}
}
/*删除指定数据的节点*/
void* list_remove(List *list, const void *data)
{
void *old_data = NULL;
Node *p_cur = list->head;
Node *p_pre = list->head;
while (p_cur != NULL && list->match(p_cur->data, data) !=0)
{
p_pre = p_cur;
p_cur = p_cur->next;
}
if(p_cur != NULL && list->match(p_cur->data, data) ==0)//删除位置在头部和中间时
{
if(p_cur == list->head)//删除位置在头部
{
list->head = p_cur->next;
if(p_cur->next == NULL)
list->tail = NULL;
}
else//中部时或尾部
{
p_pre->next = p_cur->next;
if(p_cur->next == NULL)
list->tail = p_pre;
}
old_data = p_cur->data;
free(p_cur);
list->size --;
}
return old_data;
}
/*将链表逆置*/
void list_reverse(List *list)
{
if(list_size(list) != 0)
{
Node *p_pre = list->head;
Node *p_cur = list->head->next;
list->head->next = NULL;
list->tail = list->head;
while(p_cur!= NULL)
{
p_pre = p_cur;
p_cur = p_cur->next;
p_pre->next = list->head;
list->head = p_pre;
}
}
return;
}
/*给链表排序*/
void list_sort(List *list)
{
if(list_size(list) != 0)
{
Node *p_i = list->head;
while(p_i->next != NULL)
{
Node *p_min = p_i;
Node *p_j = p_min->next;
while (p_j != NULL)
{
if(list->match(p_min->data, p_j->data) > 0)
p_min = p_j;
p_j = p_j->next;
}
if(p_min != p_i)
{
void *data = p_i->data;
p_i->data = p_min->data;
p_min->data = data;
}
p_i = p_i->next;
}
}
return;
}
/*摧毁链表*/
void list_destroy(List *list)
{
Node *p_cur = list->head;
while (p_cur != NULL)
{
list->head = list->head->next;
list->destroy(p_cur->data);//释放节点中的数据
free(p_cur);//释放节点
p_cur = list->head;
}
memset(list, 0, sizeof (List));
return;
}