亲宝软件园·资讯

展开

C语言之歌词解析

linuxcszl 人气:0

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 运行结果

0x10 全部程序

[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;
}

加载全部内容

相关教程
猜你喜欢
用户评论