C语言之歌词解析

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;
}
posted @ 2020-03-26 22:27  linuxcszl  阅读(924)  评论(0编辑  收藏  举报