Myplater项目

一、项目要求

.基于Mplayer的视频播放器

1.需求分析:

1.该程序能够加载指定路径下所有的音视频文件

(.mp3 .mp4 .avi .rm .rmvb .flv .wma)

2.能够通过界面操作视频播放器

支持:

上键

下键

回车

ESC

支持:

焦点定位

1.一级页面功能如下:

+-------------------+

| 视频播放器 |

|-------------------|

|1.查看播放列表 |

|2.开始/暂停 |

|3.停止 |

|4.上一个 |

|5.下一个 |

|6.快进 |

|7.定位 |

|8.播放方式 |

|9.退出 |

+-------------------+

查看播放列表二级页面如下:

+-------------------+

| 视频播放器 |

|-------------------|

|1.张三的歌.mp4 |

|2.李四的曲.avi |

|... |

+-------------------+

定位的二级页面

+-------------------+

| 视频播放器 |

+-------------------+

定位:XX:XX:XX

3.在一级页面启动播放列表功能,进入二级页面,

焦点定位到对应歌曲按下enter播放歌曲,按下

ESC按键能够返回一级页面

4.在一级页面启动下一个功能,能够播放下一个音

视频文件,如果到达最末尾则提示:最后一首歌曲

5.在一级页面启动上一个功能,能够播放上一个音

视频文件,如果到达最前面则提示:第一首歌曲

6.在一级页面启动快进功能,能够按倍速播放,

第一次选择启动2倍速播放

第二次选择启动4倍速播放

第三次选择返回1倍速播放(默认)

周而复始

7.在一级页面启动开始功能,

如果音视频正在播放,则暂停播放

如果音视频正在暂停,则继续播放

如果音视频文件没有播放,则按照

播放方式(1.顺序循环 2.单曲循环 3.随机播放)

播放歌曲

8.在一级页面启动停止功能:

如果音视频文件没有播放,则提示:未播放音视频文件

如果音视频正在播放,则停止当前播放内容

9.在一级页面启动定位功能:

如果定位在音视频文件播放时间内,则跳到对应位置播放

如果定位位置超过文件播放时间,则提示:定位错误

10.播放方式

在一级页面启动播放方式功能:

第一次选择启动单曲循环

第二次选择启动随机播放

第三次选择返回顺序循环(默认)

11.在一级页面启动退出功能:

则程序退出

二、思考内容:

1.如何使用颜色打印?

printf颜色打印

cpp 复制代码
printf("\033[0m \033[属性代码1;属性代码2;属性代码3...m字符串,\033[0m");
printf("\033[0m \033[格式命令如:闪烁;背景颜色;字体颜色m我是中国人 \033[0m\n");
printf("\033[0m \033[5;46;31m我是中国人!  \033[0m\n");
//每一步骤的意义

  printf("\033[0m");//清空设置
  printf("\033[5;35;43m");//填写颜色设置
 
  printf("我是中国人!\n");//要打印的内容
 
  printf("\033[0m");//清空设置

在使用完属性代码进行输出后,一定要用\033[0m重置一下,否则会影响到后续的输出。

需要注意的是用vscode进行闪烁属性的时候,无法正常闪烁,用虚拟机里面的终端就可以正常使用。

2.如何操作焦点位置?

刚开始打印的位置就是start 到end之间的内容

然后focus进行向下移动,移动到end的位置

当走到end的之后 start ,end 和focus一起向下移动。

功能示例代码如下:

cpp 复制代码
void MenuChoose(void)
{
	char ch = 0;
	int medialen = 20;

	ch = getchar();
	if ('w' == ch)
	{
		if (focus > pos_start)
		{
			focus--;
		}
		else if (focus == pos_start && pos_start == 0)
		{
			return;
		}
		else if (focus == pos_start)
		{
			pos_start--;
			pos_end--;
			focus--;
		}
	}
	else if ('s' == ch)
	{
		if (focus < pos_end)
		{
			focus++;
		}
		else if (focus == pos_end && pos_end == medialen)
		{
			return;
		}
		else if (focus == pos_end)
		{
			pos_start++;
			pos_end++;
			focus++;
		}
	}

	return;
}

3.如何从终端接收上键?下键?回车?ESC按键?

怎么接收上 下 键?上下不是一个字符。

ESC是 ^[ 看作是 : ESC + \n 对应的ascii码是27 和 10;

DOWN是^[ [B 看作是:ESC + [ + B 对应的ascii码是27 91 66

UP是^[ [B 看作是:ESC + [ + A 对应的ascii码是27 91 65

判断按键代码示例如下:

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char str[32] = {0};

	while (1)
	{
		memset(str, 0, sizeof(str));
		fgets(str, sizeof(str), stdin);
	
		if (27 == str[0] && 10 == str[1])
		{
			printf("ESC键!\n");
			return;
		}
		else if (10 == str[0])
		{
			printf("回车键!\n");
		}
		else if (27 == str[0] && 91 == str[1])
		{
			if (65 == str[2])
			{
				printf("UP键!\n");
			}
			else if (66 == str[2])
			{
				printf("DOWN键!\n");
			}
		}
	}

	return 0;
}

4.如何加载音视频文件?

调用mplyer通过音视频文件所在的路径来加载文件。

5.如何打印界面?

cpp 复制代码
#include <stdio.h>

typedef struct media
{
	int id;
	char medianame[512];
}MEDIA_CFG_S;

typedef struct menuinfo
{
	int id;
	int pos_start;
	int pos_end;
	int focus;
}MENU_CFG_S;

MEDIA_CFG_S medialist[32] = {
	{1, "张三的歌"},
	{2, "张三的歌"},
	{3, "张三的歌"},
	{4, "张三的歌"},
	{5, "张三的歌"},
	{6, "张三的歌"},
	{7, "张三的歌"},
	{8, "张三的歌"},
	{9, "张三的歌"},
	{10, "张三的歌"},
	{11, "张三的歌"},
	{12, "张三的歌"},
	{13, "张三的歌"},
	{14, "张三的歌"},
	{15, "张三的歌"},
	{16, "张三的歌"},
	{17, "张三的歌"},
	{18, "张三的歌"},
	{19, "张三的歌"},
	{20, "张三的歌"},
	{21, "张三的歌"},
};

int pos_start;		//界面起始位置
int pos_end = 10;	//界面结束位置
int focus;			//界面焦点位置
int menulevel = 2;	//一级界面

void MenuShow(void)
{
	int i = 0;
	
	if (menulevel == 2)
	{
		printf("+-----------------------------+\n");
		printf("|         音视频播放器        |\n");
		printf("|-----------------------------|\n");

		for (i = pos_start; i <= pos_end; i++)
		{
			if (i == focus)
			{
				printf("|\033[30;43m%2d.%-30s\033[0m|\n", medialist[i].id, medialist[i].medianame);
			}
			else
			{
				printf("|%2d.%-30s|\n", medialist[i].id, medialist[i].medianame);
			}
		}
		printf("+-----------------------------+\n");
	}
	else
	{
		
	}

	return;
}

void MenuChoose(void)
{
	char ch = 0;
	int medialen = 20;

	ch = getchar();
	if ('w' == ch)
	{
		if (focus > pos_start)
		{
			focus--;
		}
		else if (focus == pos_start && pos_start == 0)
		{
			return;
		}
		else if (focus == pos_start)
		{
			pos_start--;
			pos_end--;
			focus--;
		}
	}
	else if ('s' == ch)
	{
		if (focus < pos_end)
		{
			focus++;
		}
		else if (focus == pos_end && pos_end == medialen)
		{
			return;
		}
		else if (focus == pos_end)
		{
			pos_start++;
			pos_end++;
			focus++;
		}
	}

	return;
}

int main(int argc, const char *argv[])
{
	while (1)
	{
		system("clear");
		MenuShow();
		MenuChoose();
	}

	return 0;
}

这里就是打印界面,每按一次上下键都会打印一次完整的显示,所以调用system("clear");将之前打印的清除掉,然后在进行打印,这样就只保存了最后一次的打印。

6.如何存储所有的音视频文件?

用一个二维的数组,每一行都存储一个音视频文件的路径

7.如何控制mplayer播放器?

fork + exec来进行播放,也就是创建子进程来播放mplayer。

示例代码如下:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	pid_t pid;
	int fd = 0;
	char tmpbuff[1024] = {0};

	pid = fork();
	if (-1 == pid)
	{
		perror("fail to fork");
		return -1;
	}
	if (0 == pid)
	{
		execlp("mplayer", "mplayer", "-slave", "-input",  "file=/tmp/myfifo", "../1.flv", "-quiet", NULL);
	}
	else if (pid > 0)
	{
		fd = open("/tmp/myfifo", O_RDWR);
		if (-1 == fd)
		{
			perror("fail to open");
			return -1;
		}

		while (1)
		{
			fgets(tmpbuff, sizeof(tmpbuff), stdin);
			//pause\n
			write(fd, tmpbuff, strlen(tmpbuff));
		}	
		close(fd);
	}
	
	return 0;
}

那怎么控制mplayer?

在mplayer工作的时候,可以指定一个从属模式设置一个管道,打开这个管道然后写命令。

三、软件编写

1.查看播放列表:在指定目录下遍历文件,找到以媒体文件结尾的文件名,将其路径存储在变量中

2.开始/暂停: fork + exec(mplayer)

3.停止:向管道中写入命令

4.上一首:

5.下一首:

6.快进:向管道中写入命令

7.定位:向管道中写入命令

8.播放方式:SIGCHLD证明有子进程结束了

9.退出:进程任务结束,如果mplayer正在播放,将mplayer结束

1.head.h

cpp 复制代码
#ifndef __HEAD_H__
#define __HEAD_H__

extern int LoadMediaFile(char *pDirName);
extern void ShowMediaList(void);
extern int StopMedia(void);
extern int PlayMedia(int MediaIndex);

extern int ExecUserChoose(int No);
extern int GetUserChoose(void);
extern void ShowMenu(void);
extern int PauseMedia(void);
extern int SetMediaSpeed(void);
extern int SetMediaPosition(void);
extern void SwitchMediaMode(void);
extern void sigchild_handler(int signo);

extern char MediaList[100][256];             //存放媒体文件列表
extern int CurMediaLen;                        //当前加载的媒体文件个数

//是否为播放上一首
extern int IsPlayPre;

//是否为播放下一首
extern int IsPlayNext;

#endif

2.main.c

cpp 复制代码
/***************************************************
 * 
 * 主函数
 * 
 **************************************************/
#include <stdio.h>
#include "head.h"

int main(void)
{
    int No = 0;

    while (1)
    {
        CurMediaLen = LoadMediaFile("/home/linux/Music");
        ShowMenu();
        No = GetUserChoose();
        if (9 == No)
        {

            break;
        }
        ExecUserChoose(No);
    }
    
    return 0;
}

3.menu.c

cpp 复制代码
/***************************************************
 * 
 * 界面相关代码
 * 
 **************************************************/

#include "head.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

/***************************************************
 *函数名:ShowMenu
 *功 能:
 *      显示界面
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void ShowMenu(void)
{
    printf("===========================================\n");
    printf("1.查看播放列表\n");
    printf("2.开始/暂停\n");
    printf("3.停止\n");
    printf("4.上一首\n");
    printf("5.下一首\n");
    printf("6.快进\n");
    printf("7.定位\n");
    printf("8.播放方式\n");
    printf("9.退出\n");
    printf("===========================================\n");
    
    return;
}

/***************************************************
 *函数名:GetUserChoose
 *功 能:
 *      获得用户的选择
 *参数:
 *      void
 *返回值:
 *      用户选择的选项编号
 ***************************************************/ 
int GetUserChoose(void)
{
    int No = 0;

    printf("请选择:\n");
    scanf("%d", &No);

    return No;
}

/***************************************************
 *函数名:PlayNext
 *功 能:
 *      播放下一首
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void PlayNext(void)
{
    StopMedia();
    IsPlayNext = 1;

    return;
}

/***************************************************
 *函数名:PlayPre
 *功 能:
 *      播放下一首
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void PlayPre(void)
{
    StopMedia();
    IsPlayPre = 1;

    return;
}

/***************************************************
 *函数名:ExecUserChoose
 *功 能:
 *      执行用户选择
 *参数:
 *      No: 用户选择的选项编号
 *返回值:
 *      成功返回0 
 *      失败返回-1 
 ***************************************************/ 
int ExecUserChoose(int No)
{
    if (1 == No)
    {
        ShowMediaList();
    }
    else if (2 == No)
    {
        PauseMedia();
    }
    else if (3 == No)
    {
        signal(SIGCHLD, SIG_DFL);
        StopMedia();
        wait(NULL);
        signal(SIGCHLD, sigchild_handler);
    }
    else if (4 == No)
    {
        PlayPre();
    }
    else if (5 == No)
    {
        PlayNext();
    }
    else if (6 == No)
    {
        SetMediaSpeed();
    }
    else if (7 == No)
    {
        SetMediaPosition();
    }
    else if (8 == No)
    {
        SwitchMediaMode();
    }
    else if (9 == No)
    {
        signal(SIGCHLD, SIG_DFL);
        StopMedia();
    }

    return 0;
}

4.mplayer.c

cpp 复制代码
/***************************************************
 * 
 * 媒体相关代码
 * 
 **************************************************/

#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include "head.h"

//存放媒体文件列表
char MediaList[100][256] = {0};  

//当前加载的媒体文件个数           
int CurMediaLen = 0;                        

//当前播放的媒体的编号
int CurMediaIndex = 0;

//Mplayer播放方式
typedef enum MediaMode
{
    MPLAYER_MODE_NEXT = 1,
    MPLAYER_MODE_SINGLE = 2,
    MPLAYER_MODE_RANDOM = 3,
}MPLAYER_MODE_EN;

//当前播放模式
MPLAYER_MODE_EN CurMediaMode = MPLAYER_MODE_NEXT;

//Mplayer播放速度
typedef enum MediaSpeed 
{
    MPLAYER_SPEED_ONE = 1,
    MPLAYER_SPEED_TWO = 2,
    MPLAYER_SPEED_FOUR = 4,
}MPLAYER_SPEED_EN;

//当前播放速度
MPLAYER_SPEED_EN CurMediaSpeed = MPLAYER_SPEED_ONE;

//Mplayer播放状态
typedef enum MediaStat
{
    MPLAYER_STAT_FREE,          //空闲状态
    MPLAYER_STAT_PLAY,          //播放状态
    MPLAYER_STAT_PAUSE,         //暂停状态
}MPLAYER_STAT_EN;

//当前播放状态
MPLAYER_STAT_EN CurMediaStat = MPLAYER_STAT_FREE;

//是否为播放上一首
int IsPlayPre = 0;

//是否为播放下一首
int IsPlayNext = 0;

/***************************************************
 *函数名:IsMediaFile
 *功 能:
 *      判断是否为媒体文件
 *参数:
 *      pfilename: 文件名
 *返回值:
 *      成功返回 1
 *      失败返回 0 
 ***************************************************/ 
int IsMediaFile(char *pfilename)
{
    char *ptmp = NULL;

    //helloworld.mp4
    ptmp = pfilename + strlen(pfilename);
    while (ptmp >= pfilename && *ptmp != '.')
    {
        ptmp--;
    }

    if (!strcmp(ptmp, ".mp3") || !strcmp(ptmp, ".mp4") || !strcmp(ptmp, ".flv"))
    {
        return 1;
    }

    return 0;
}

/***************************************************
 *函数名:LoadMediaFile
 *功 能:
 *      加载指定目录下所有的媒体(.mp3、.mp4、.avi、.flv)文件
 *参数:
 *      pDirName: 存放媒体文件的目录
 *返回值:
 *      成功返回实际加载媒体文件个数
 *      失败返回 -1 
 ***************************************************/ 
int LoadMediaFile(char *pDirName)
{
    //1.遍历目录文件下的所有文件名
    DIR *dp = NULL;
    struct dirent *pp = NULL;
    int cnt = 0;

    dp = opendir(pDirName);
    if (NULL == dp)
    {
        return -1;
    }

    while (1)
    {
        pp = readdir(dp);
        if (NULL == pp)
        {
            break;
        }

        if ('.' == *pp->d_name)
        {
            continue;
        }

        if (IsMediaFile(pp->d_name))
        {
            sprintf(MediaList[CurMediaLen], "%s/%s", pDirName, pp->d_name);
            CurMediaLen++;
            cnt++;
        }
    }

    closedir(dp);

    return cnt;
}

/***************************************************
 *函数名:sigchild_handler
 *功 能:
 *      播放完毕处理函数
 *参数:
 *      signo:信号的编号
 *返回值:
 *      void
 ***************************************************/ 
void sigchild_handler(int signo)
{
    wait(NULL);

    if (IsPlayNext)
    {
        if (CurMediaIndex == CurMediaLen)
        {
            printf("已经到最末尾了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex+1);
        }  
        IsPlayNext = 0;

        return;
    }
    
    if (IsPlayPre)
    {
        if (CurMediaIndex == 0)
        {
            printf("已经到最开头了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex-1);
        }  
        IsPlayPre = 0;

        return;
    }

    if (CurMediaMode == MPLAYER_MODE_NEXT)
    {
        if (CurMediaIndex == CurMediaLen)
        {
            printf("已经到最末尾了!\n");
            return;
        }
        else 
        {
            PlayMedia(CurMediaIndex+1);
        }  
    }
    else if (CurMediaMode == MPLAYER_MODE_SINGLE)
    {
        PlayMedia(CurMediaIndex);
    }
    else if (CurMediaMode == MPLAYER_MODE_RANDOM)
    {
        srand(time(NULL));
        PlayMedia(rand() % CurMediaLen);
    }
    
    return;
}

/***************************************************
 *函数名:PlayMedia
 *功 能:
 *      播放媒体文件
 *参数:
 *      MediaIndex:媒体文件的编号
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int PlayMedia(int MediaIndex)
{
    pid_t pid;

    signal(SIGCHLD, sigchild_handler);

    pid = fork();
    if (-1 == pid)
    {
        return -1;
    }
    if (0 == pid)
    {       
        mkfifo("/tmp/myfifo", 0664);
        close(1);
        close(2);
        execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", MediaList[MediaIndex], NULL);
    }

    CurMediaIndex = MediaIndex; 
    CurMediaStat = MPLAYER_STAT_PLAY;
    CurMediaSpeed = MPLAYER_SPEED_ONE;
    
    return 0;
}

/***************************************************
 *函数名:ShowMediaList
 *功 能:
 *      打印媒体列表
 *参数:
 *      void
 *返回值:
 *      void
 ***************************************************/ 
void ShowMediaList(void)
{
    int i = 0;
    int No = -1;

    for (i = 0; i < CurMediaLen; i++)
    {
        printf("%d.%s\n", i+1, MediaList[i]);
    }

    while (1)
    {
        printf("请选择播放的歌曲编号(输入0返回上一级)\n");
        scanf("%d", &No);
        if (0 == No)
        {
            break;
        }

        PlayMedia(No-1);
    }

    return;
}

/***************************************************
 *函数名:StopMedia
 *功 能:
 *      停止播放媒体文件
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int StopMedia(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    write(fd, "stop\n", 5);
    CurMediaStat = MPLAYER_STAT_FREE;

    close(fd);

    return 0;
}

/***************************************************
 *函数名:PauseMedia
 *功 能:
 *      暂停媒体文件
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int PauseMedia(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    write(fd, "pause\n", 6);

    close(fd);

    if (MPLAYER_STAT_PLAY == CurMediaStat)
    {
        CurMediaStat = MPLAYER_STAT_PAUSE;
    }
    else if (MPLAYER_STAT_PAUSE == CurMediaStat)
    {
        CurMediaStat = MPLAYER_STAT_PLAY;
    }

    return 0;
}

/***************************************************
 *函数名:SetMediaSpeed
 *功 能:
 *      设置媒体播放速度
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int SetMediaSpeed(void)
{   
    int fd = 0;

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    if (MPLAYER_SPEED_ONE == CurMediaSpeed)
    {
        write(fd, "speed_set 2\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_TWO;
    }
    else if (MPLAYER_SPEED_TWO == CurMediaSpeed)
    {
        write(fd, "speed_set 4\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_FOUR;
    }
    else if (MPLAYER_SPEED_FOUR == CurMediaSpeed)
    {
        write(fd, "speed_set 1\n", 12);
        CurMediaSpeed = MPLAYER_SPEED_ONE;
    }

    close(fd);

    return 0;
}

/***************************************************
 *函数名:SetMediaPosition
 *功 能:
 *      设置媒体播放位置
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
int SetMediaPosition(void)
{   
    int fd = 0;
    int n = 0;
    char cmdbuf[1024] = {0};

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        return -1;
    }

    printf("请输入秒数:\n");
    scanf("%d", &n);
    sprintf(cmdbuf, "seek %d 2\n", n);
    write(fd, cmdbuf, strlen(cmdbuf));

    close(fd);

    return 0;
}

/***************************************************
 *函数名:SwitchMediaMode
 *功 能:
 *      切换媒体播放模式
 *参数:
 *      void
 *返回值:
 *      成功返回 0
 *      失败返回 -1 
 ***************************************************/ 
void SwitchMediaMode(void)
{
    if (CurMediaMode == MPLAYER_MODE_NEXT)
    {
        printf("切换为:单曲循环\n");
        CurMediaMode = MPLAYER_MODE_SINGLE;
    }
    else if (CurMediaMode == MPLAYER_MODE_SINGLE)
    {
        printf("切换为:随机播放\n");
        CurMediaMode = MPLAYER_MODE_RANDOM;
    }
    else if (CurMediaMode == MPLAYER_MODE_RANDOM)
    {
        printf("切换为:顺序播放\n");
        CurMediaMode = MPLAYER_MODE_NEXT;
    }

    return;
}

5.makefile

cpp 复制代码
a.out:main.c menu.c mplayer.c 
	gcc $^ -o $@

四、工作方式

相关推荐
GIS学姐嘉欣12 小时前
DeepSeek预测25考研分数线
前端·考研·gis·webgis
唐天下文化3 天前
全国哪些考研机构比较好?
考研
计软考研大C哥3 天前
软件考研,选择华科还是科软?
考研
Nicn5 天前
考研操作系统----操作系统的概念定义功能和目标(仅仅作为王道哔站课程讲义作用)
c++·考研·系统架构·操作系统·计算机组成原理·os
DHX~6 天前
数据结构(考研)
数据结构·考研
岑梓铭8 天前
计算机网络第九章——数据链路层《概念、组帧、差错控制、可靠传输...》
网络·网络协议·计算机网络·考研·408
一个 00 后的码农9 天前
25自动化考研复试面试常见核心问题真题汇总,自动化考研复试面试有哪些经典问题?自动化考研复试难不难啊?
经验分享·笔记·考研·面试
羊小猪~~10 天前
MYSQL学习笔记(七):新年第一篇之子查询
数据库·笔记·后端·sql·学习·mysql·考研
666HZ66610 天前
操作系统2.2
考研