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;
}
C语言之歌词解析的更多相关文章
- android 歌词解析
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F ...
- OC3_歌词解析
// // LrcManager.h // OC3_歌词解析 // // Created by zhangxueming on 15/6/15. // Copyright (c) 2015年 zhan ...
- ios开发学习- 简易音乐播放器2 (基于iPhone4s屏幕尺寸)-- 歌词解析--plist文件应用--imageNamed图片加载耗内存
声明:(部分图片来自网络,如果侵犯了您的权益请联系我,会尽快删除!) 又是音乐播放器,不过这次和上次不一样了,准确说这次更像播放器了,初学者不建议看这个,可以先看前面一个音乐播放器(1),当然 我没加 ...
- C语言文件操作解析(五)之EOF解析(转载)
C语言文件操作解析(五)之EOF解析 在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符.但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特 ...
- 【转】C语言文件操作解析(三)
原文网址:http://www.cnblogs.com/dolphin0520/archive/2011/10/07/2200454.html C语言文件操作解析(三) 在前面已经讨论了文件打开操作, ...
- iOS 音乐播放器之锁屏效果+歌词解析
概述 功能描述:锁屏歌曲信息.控制台远程控制音乐播放:暂停/播放.上一首/下一首.快进/快退.列表菜单弹框和拖拽控制台的进度条调节进度(结合了QQ音乐和网易云音乐在锁屏状态下的效果).歌词解析并随音乐 ...
- iOS电商常见动画与布局、微信悬浮窗、音乐播放器、歌词解析、拖动视图等源码
iOS精选源码 MXScroll 介绍 混合使用UIScrollView ios 电商demo(实现各种常见动画效果和页面布局) 一行代码集成微信悬浮窗 可拖动,大小的视图,可放置在屏幕边缘. 在使用 ...
- C语言创建及解析Json的使用法则
参考原文:http://blog.csdn.net/xukai871105/article/details/33013455 JSON(JavaScriptObject Notation)是一种轻量级 ...
- 在浏览器的背后(二) —— HTML语言的语法解析
当你看到这篇文章意味着我辜负了@教主的殷切期望周末木有去约会,以及苏老师@我思故我在北京鼓楼的落井下石成功了…… 本文demo powered by 已经结婚的@老赵的不再维护的wind.js 物是人 ...
随机推荐
- 云服务器离线安装MariaDB安装步骤和解决办法
前面我写了tomcat的安装那么接下来我们来安装云服务的数据库服务 第一步:下载安装包 https://downloads.mariadb.org/ 按照上图所示操作就能完成在线安装,但由于国内的网络 ...
- zookeeper 客户端连接报: Will not attempt to authenticate using SASL
解决方法:我在学习zk的时候,用客户端连接zk,发现接收不到watch通知,并且报 如图所示错误: 后查看服务没问题:图示 后查看防火墙状态,关闭防火墙 连接后正常: 如果查看防火墙状态是dead,s ...
- rpm报错warning: /var/tmp/rpm-tmp.1OZa8q: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY的解决
参考链接:http://blog.51cto.com/zymin0823/1546537 报错: 解决:使用如下两个选项
- 让git push命令不再需要密码
最近利用jekyll写博客,为的就是博客管理方便,但是在上传博客的时候使用git push命令每次都得输入github帐号和密码特别的不方便,于是就搜了一下. 在这篇文章里提到,GitHub获得远程库 ...
- linux中nginx、mysql安装碰到的问题
服务器到期新买了一台服务器,记录一下重新安装基本环境碰到了一些问题 安装nginx 1. 启动失败 403 forbidden nginx 解决方案:(个人使用直接用了root账号,修改对应nginx ...
- plsql-工具安装部署及使用配置
参考文档链接:https://blog.csdn.net/li66934791/article/details/83856225 简介: PL/SQL Developer是一个集成开发环境,专门开发面 ...
- 使用thymeleaf模板实现博客评论的异步刷新
使用thymeleaf模板实现博客评论的异步刷新 最简单的一个要求:用户可以在博客下面进行评论,然后评论后点击提交后直接上传到数据库,并且局部刷新 这是前端页面的展示,使用的semanticUI进行构 ...
- java快速开发平台可视化开发表单
XJR java快速开发平台,简单的理解就是:开发人员以某种编程语言或者某几种编程语言(比如:目前流行的多种web技术,包括springboot, JPA,Druid, Activiti,Lombok ...
- js中如何判断属性是对象实例中的属性还是原型中的属性
ECMAScript5中的hasOwnProperty()方法,用于判断只在属性存在与对象实例中的时候,返回true,in操作符只要通过对象能访问到属性就返回true. 因此只要in操作符返回true ...
- BLAKE及BLAKE2算法详解
1 简介 哈希算法 (Hash Algorithm) 是将任意长度的数据映射为固定长度数据的算法,也称为消息摘要.一般情况下,哈希算法有两个特点: 原始数据的细微变化(比如一个位翻转)会导致结果产生巨 ...