C语言 - 你一定能看懂的三子棋详解(万字长文,傻瓜式解析)

news/2024/7/5 2:57:54

三子棋详解

文章目录

  • 一.写在前面
  • 二.效果预览
  • 三.整体思路
  • 四.开始制作
    • 1. 打印游戏菜单
    • 2.用 do while 语句实现玩家作出选择
    • 3.进入游戏
    • 4.打印一个 3*3 的棋盘
    • 5.对战开始
    • 6.判断输赢

一.写在前面

☀️1.制作目的
三子棋是一个有趣的小游戏,我们在学习编程中尝试制作这样有趣的小游戏更能激发我们学习编程的兴趣以及和信心。

☁️2.代码虽长但易理解
为了使得读者更易看懂,我在游戏代码中加入了大量的注释,这使得文章中的代码量可能有点长,但更易理解。
此外,文章中可能文字描述较少,我将各种解释放在了代码的注释中

⚡️3.采用宏定义
在工程中,我使用了宏定义,将ROW(行)和 COL(列)定义为3使得代码的可读性和扩展性更高,不仅方便理解,且易于修改。

❄️4.清屏函数
为了令界面看起来更加清爽,我在棋盘打印的函数中加入了清屏函数。

📢5.文章或代码中某些名词解释如下

  • ROW 和 COL 表示行和列
  • menu 表示游戏菜单
  • InitBoard 表示棋盘数组的初始化
  • DisplayBoard 表示棋盘的展示
  • PlayerMove 表示玩家下棋
  • ComputerMove 表示电脑下棋
  • IsWin 表示输赢的判断

二.效果预览

🔆运行程序
在这里插入图片描述

🔆输入数字 1 ,进入游戏
在这里插入图片描述

🔆输入下棋坐标,进行下棋,玩家下的棋为 X ,电脑下的棋为 O
比如我输入坐标 2 2,电脑随机下棋得到下图
在这里插入图片描述

🔆继续下棋,直到游戏胜利
在这里插入图片描述

三.整体思路

🚀 1. 打印游戏菜单
🚀 2. 玩家作出选择,进入游戏
🚀 3. 定义棋盘,并初始化为空格
🚀 4. 打印棋盘
🚀 5. 进入对战,玩家和电脑下棋
🚀6. 判断胜负

四.开始制作

1. 打印游戏菜单

用一个函数打印游戏菜单,玩家输入 1 表示进入游戏,玩家输入 0 表示离开游戏。

void menu()
{
	printf("*************************\n");
	printf("*****    1. play    *****\n");
	printf("*****    0. exit    *****\n");
	printf("*************************\n");
}

菜单效果如下:
在这里插入图片描述

2.用 do while 语句实现玩家作出选择

玩家若选择 1 ,进入游戏。
玩家若选择 0 ,退出游戏。
玩家若输入其他数字,重新作出选择。

主函数如下:

int main()
{
	// 定义一个用来做选择的数
	int input = 0;
	
	// 利用do while语句来作出选择
	do
	{
		// 打印菜单
		menu();
		// 输入0或1来作出选择,1表示开始游戏,0表示离开游戏
		scanf("%d", &input);
		// 进入你所作出的选择中
		switch (input)
		{
		case 1:
			// 进入游戏
			game();
			break;
		case 0:
			// 离开游戏
			break;
		default:
			// 如果玩家输入了其他数字,提醒玩家输入错误,要求玩家重新输入
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input != 1 && input != 0);

	return 0;
}

3.进入游戏

注:将 ROW 和 COL 定义为 3

// 用 ROW 表示棋盘的行,用 COL 表示棋盘的列
#define ROW 3
#define COL 3

进入游戏后,先定义一个二维数组,作为棋盘。

	// 进入游戏后,第一件要做的事就是定义棋盘
	char Board[ROW][COL];

然后用一个初始化函数将这个二维数组的所有元素初始化为空格

	// 定义棋盘后,因为还未开始下棋,所以需用一个函数将棋盘的每个元素初始化为空格
	InitBoard(Board, ROW, COL);
void InitBoard(char Board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			Board[i][j] = ' ';
		}
	}
}

4.打印一个 3*3 的棋盘

在打印之前使用 system函数(引用头文件stdlib,h) 清空屏幕
由于棋盘具有行和列,那么也就需要利用两个循环来打印每行每列的元素
为了美观,当然不能只打印一些空格出来,利用各种符号来模拟出棋盘的边界也是很重要的。

void DisplayBoard(char Board[ROW][COL], int row, int col)
{
	// 为了使每次打印棋盘更加清爽一点,我们在打印之前清空屏幕
	system("cls");

	// 定义 i 与 j 进行打印棋盘
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		// 以此来打印每行的分隔行
		printf("*---*---*---*\n"); 
		// 打印每一行
		printf("|");
		for (j = 0; j < col; j++)
		{
			printf(" %c ", Board[i][j]);
			printf("|");
		}
		// 每一行打印完后进行换行
		printf("\n");
	}
	// 待循环走完后再打印一行来使棋盘美观
	printf("*---*---*---*\n");

}

效果如下:
在这里插入图片描述

5.对战开始

打印棋盘后玩家与电脑开始对战
利用 while 循环,胜负未分时循环继续,胜负已分时结束循环
同时,在 while 循环中,我们用一个 iswin 函数和 if 语句来判断游戏的输赢

对战过程如下:

	char ret = 0;
	while (1)
	{
		// 我们利用一个函数让玩家先手下棋
		PlayerMove(Board, ROW, COL);
		// 在玩家下棋成功后,打印棋盘。
		DisplayBoard(Board, ROW, COL);
		// 用ret函数接受函数的返回值,根据返回值来判断对战情况
		ret = IsWin(Board, ROW, COL);
		// 此处用if语句,除了ret为'C'时,其他情况都是游戏结束,跳出循环
		if (ret != 'C')
		{
			break;
		}
		
		// 在玩家下棋后让电脑下棋
		ComputerMove(Board, ROW, COL);
		// 在电脑下棋成功后,打印棋盘。
		DisplayBoard(Board, ROW, COL);
		// 用ret接受函数的返回值,根据返回值来判断对战情况
		ret = IsWin(Board, ROW, COL);
		// 此处用if语句,除了ret为'C'时,其他情况都是游戏结束,跳出循环
		if (ret != 'C')
		{
			break;
		}
	}

玩家下棋

规定玩家下的棋为 X 。
玩家下棋时需让玩家输入要落子的坐标,输入完成成后程序判断玩家输入的坐标是否合法以及是否被占用,若合法且未被占用,下棋成功;若不合法或者坐标已被占用,提醒玩家重新输入。
这里可以用到 while 循环,当下棋成功时跳出循环,当下棋失败时循环结束
代码如下:

void PlayerMove(char Board[ROW][COL], int row, int col)
{
	// 因为玩家下的棋可能不合法,所以我们此时应使用while循坏
	// 在玩家下棋合法时跳出循环,不合法时在循环内让玩家继续输入坐标下棋
	while (1)
	{
		// 玩家应输入坐标来下棋(如输入 2 1 表示下棋在第二行第一列)
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		// 在玩家下棋后判断玩家下棋是否合法。如果合法,下棋成功;如果不合法,重新输入。
		// 判断合法的步骤如下(两个步骤,所以最好用两个if):
		// 1.判断玩家输入的坐标是否在规定范围内
		// 2.判断玩家输入的那个坐标是否被占用:
		//		因为定义棋盘的数组是从0开始,玩家输入的坐标应从1开始
		//		所以判断玩家输入的坐标是否合法时应将玩家输入的x坐标和y坐标都减1
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
		{
			if (Board[x - 1][y - 1] == ' ')
			{
				// 如果合法,改变数组的元素
				// 我们规定,玩家下的棋为'X'
				Board[x - 1][y - 1] = 'X';

				// 改变元素后跳出while循环
				break;
			}
			else
			{
				// 如果该位置被占用,提醒玩家重新输入
				printf("你输入的坐标被占用,请重新输入一个坐标\n");
			}
		}
		else
		{
			// 如果玩家输入的坐标不在棋盘内,提醒玩家重新输入
			printf("你输入的坐标不在棋盘内,请重新输入\n");
		}
	}
}

电脑下棋

电脑下棋时需在程序中生成两个随机数来模拟电脑下棋,我们可以使用时间戳来完成这一任务
首先需引用两个头文件

// 时间戳的头文件
#include <stdlib.h>
#include <time.h>

我们将生成的随机数模 2 ,就可以转化为0~2的随机数。这样,便不用判断坐标是否合法。
但仍需判断坐标是否被占用。
将电脑下的棋规定为 O
和玩家下棋一样,这里也要用到 while 循环,当下棋成功时跳出循环,当下棋失败时循环结束。
代码如下:

void ComputerMove(char Board[ROW][COL], int row, int col)
{
	// 用时间戳生成随机数来模拟电脑随机下棋
	srand((unsigned int)time(NULL));

	// 由于电脑下棋的坐标可能被占用,所以此时需用一个循环
	// 当下棋成功跳出循坏,当下棋失败循环继续
	while (1)
	{
		// 将时间戳余3即可得到0~2的随机数
		int x = rand() % 3;
		int y = rand() % 3;

		// 判断随机生成的坐标是否被占用,若被占用,重新生成;若未占用,改变棋盘的数组并跳出循环。
		// 由于生成的数一定是0~2,所以只用判断是否被占用即可。
		if (Board[x][y] == ' ')
		{
			Board[x][y] = 'O';
			break;
		}
	}
}

6.判断输赢

在每次玩家下棋和电脑下棋之后,都要用一个 IsWin 函数来查看对战情况。
我们用 ret 来接受该函数的返回值。用 X 表示玩家胜利,用 O 表示电脑胜利,用 C 表示游戏继续,用 D 表示平局。
首先看是否有三个连着的相同的元素(元素不为空格)
(1)判断每行
(2)判断每列
(3)判断对角线

	int i = 0;

	// 首先判断一整行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < row; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[i][0] == Board[i][1] && Board[i][0] == Board[i][2] && Board[i][0] != ' ')
		{
			return Board[i][0];
		}
	}

	// 再判断一竖行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < col; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[0][i] == Board[1][i] && Board[0][i] == Board[2][i] && Board[0][i] != ' ')
		{
			return Board[0][i];
		}
	}

	// 最后判断两个对角线的元素是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	if (Board[0][0] == Board[1][1] && Board[0][0] == Board[2][2] && Board[0][0] != ' ')
	{
		return Board[0][0];
	}
	if (Board[0][2] == Board[1][1] && Board[0][2] == Board[2][0] && Board[0][2] != ' ')
	{
		return Board[0][2];
	}

如果在判断完之后函数还未结束,说明胜负未分,此时要么是游戏还未结束,要么是游戏平局(所有的格子都没占满)。
所以可以遍历这个二维数组,如果遇到空格说明游戏还未结束,返回 C
在遍历完成后若函数还未结束,说明所有的空格已被占满。此时游戏结束,返回 D
代码实现如下:

	// 此处遍历棋盘,如果棋盘中还有空格则令游戏继续
	for (i = 0; i < row; i++)
	{	
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')
			{
				// 此处返回 C 表示英语单词continue,即游戏继续
				return 'C';
			}
		}
	}

	// 如果游戏未分胜负,棋盘中又没有空位,则返回 D 表示英语单词draw,即游戏平局
	return 'D';

该函数整体代码如下

char IsWin(char Board[ROW][COL], int row, int col)
{
	int i = 0;

	// 首先判断一整行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < row; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[i][0] == Board[i][1] && Board[i][0] == Board[i][2] && Board[i][0] != ' ')
		{
			return Board[i][0];
		}
	}

	// 再判断一竖行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < col; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[0][i] == Board[1][i] && Board[0][i] == Board[2][i] && Board[0][i] != ' ')
		{
			return Board[0][i];
		}
	}

	// 最后判断两个对角线的元素是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	if (Board[0][0] == Board[1][1] && Board[0][0] == Board[2][2] && Board[0][0] != ' ')
	{
		return Board[0][0];
	}
	if (Board[0][2] == Board[1][1] && Board[0][2] == Board[2][0] && Board[0][2] != ' ')
	{
		return Board[0][2];
	}

	// 如果函数还能运行到这里,说明胜负还未分
	// 胜负未分有两种情况,一种是棋盘还没占满,下棋进行;一种是棋盘已占满(即平局)。

	// 此处遍历棋盘,如果棋盘中还有空格则令游戏继续
	for (i = 0; i < row; i++)
	{	
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')
			{
				// 此处返回 C 表示英语单词continue,即游戏继续
				return 'C';
			}
		}
	}

	// 如果游戏未分胜负,棋盘中又没有空位,则返回 D 表示英语单词draw,即游戏平局
	return 'D';
}

在玩家与电脑下棋结束后,就需要判断是玩家赢了还是电脑赢了,这里使用 switch case 语句输出相应的内容:

	// 在while循环结束后,即对战结束后,判断玩家赢还是电脑赢,又或是平局
	switch (ret)
	{
	case 'X':
		printf("恭喜你,你赢了!\n");
		break;
	case 'O':
		printf("游戏结束,你输了。\n");
		break;
	case 'D':
		printf("平局\n");
		break;
	}

游戏到此结束!

工程整体代码如下

#pragma warning (disable:6031)
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

// 时间戳的头文件
#include <stdlib.h>
#include <time.h>

// 用 ROW 表示棋盘的行,用 COL 表示棋盘的列
#define ROW 3
#define COL 3

void menu()
{
	printf("*************************\n");
	printf("*****    1. play    *****\n");
	printf("*****    0. exit    *****\n");
	printf("*************************\n");
}

void InitBoard(char Board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			Board[i][j] = ' ';
		}
	}
}

void DisplayBoard(char Board[ROW][COL], int row, int col)
{
	// 为了使每次打印棋盘更加清爽一点,我们在打印之前清空屏幕
	system("cls");

	// 定义 i 与 j 进行打印棋盘
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		// 以此来打印每行的分隔行
		printf("*---*---*---*\n"); 
		// 打印每一行
		printf("|");
		for (j = 0; j < col; j++)
		{
			printf(" %c ", Board[i][j]);
			printf("|");
		}
		// 每一行打印完后进行换行
		printf("\n");
	}
	// 待循环走完后再打印一行来使棋盘美观
	printf("*---*---*---*\n");

}

void PlayerMove(char Board[ROW][COL], int row, int col)
{
	// 因为玩家下的棋可能不合法,所以我们此时应使用while循坏
	// 在玩家下棋合法时跳出循环,不合法时在循环内让玩家继续输入坐标下棋
	while (1)
	{
		// 玩家应输入坐标来下棋(如输入 2 1 表示下棋在第二行第一列)
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		// 在玩家下棋后判断玩家下棋是否合法。如果合法,下棋成功;如果不合法,重新输入。
		// 判断合法的步骤如下(两个步骤,所以最好用两个if):
		// 1.判断玩家输入的坐标是否在规定范围内
		// 2.判断玩家输入的那个坐标是否被占用:
		//		因为定义棋盘的数组是从0开始,玩家输入的坐标应从1开始
		//		所以判断玩家输入的坐标是否合法时应将玩家输入的x坐标和y坐标都减1
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
		{
			if (Board[x - 1][y - 1] == ' ')
			{
				// 如果合法,改变数组的元素
				// 我们规定,玩家下的棋为'X'
				Board[x - 1][y - 1] = 'X';

				// 改变元素后跳出while循环
				break;
			}
			else
			{
				// 如果该位置被占用,提醒玩家重新输入
				printf("你输入的坐标被占用,请重新输入一个坐标\n");
			}
		}
		else
		{
			// 如果玩家输入的坐标不在棋盘内,提醒玩家重新输入
			printf("你输入的坐标不在棋盘内,请重新输入\n");
		}
	}
}

void ComputerMove(char Board[ROW][COL], int row, int col)
{
	// 用时间戳生成随机数来模拟电脑随机下棋
	srand((unsigned int)time(NULL));

	// 由于电脑下棋的坐标可能被占用,所以此时需用一个循环
	// 当下棋成功跳出循坏,当下棋失败循环继续
	while (1)
	{
		// 将时间戳余3即可得到0~2的随机数
		int x = rand() % 3;
		int y = rand() % 3;

		// 判断随机生成的坐标是否被占用,若被占用,重新生成;若未占用,改变棋盘的数组并跳出循环。
		// 由于生成的数一定是0~2,所以只用判断是否被占用即可。
		if (Board[x][y] == ' ')
		{
			Board[x][y] = 'O';
			break;
		}
	}
}

char IsWin(char Board[ROW][COL], int row, int col)
{
	int i = 0;

	// 首先判断一整行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < row; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[i][0] == Board[i][1] && Board[i][0] == Board[i][2] && Board[i][0] != ' ')
		{
			return Board[i][0];
		}
	}

	// 再判断一竖行是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	for (i = 0; i < col; i++)
	{
		// 判断是否存在三个元素中两两相同且不为空格
		if (Board[0][i] == Board[1][i] && Board[0][i] == Board[2][i] && Board[0][i] != ' ')
		{
			return Board[0][i];
		}
	}

	// 最后判断两个对角线的元素是否有连着的相同的'X'或者'O',如果有则返回'X'或者'O'
	if (Board[0][0] == Board[1][1] && Board[0][0] == Board[2][2] && Board[0][0] != ' ')
	{
		return Board[0][0];
	}
	if (Board[0][2] == Board[1][1] && Board[0][2] == Board[2][0] && Board[0][2] != ' ')
	{
		return Board[0][2];
	}

	// 如果函数还能运行到这里,说明胜负还未分
	// 胜负未分有两种情况,一种是棋盘还没占满,下棋进行;一种是棋盘已占满(即平局)。

	// 此处遍历棋盘,如果棋盘中还有空格则令游戏继续
	for (i = 0; i < row; i++)
	{	
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')
			{
				// 此处返回 C 表示英语单词continue,即游戏继续
				return 'C';
			}
		}
	}

	// 如果游戏未分胜负,棋盘中又没有空位,则返回 D 表示英语单词draw,即游戏平局
	return 'D';
}

void game()
{
	// 进入游戏后,第一件要做的事就是定义棋盘
	char Board[ROW][COL];
	
	// 定义棋盘后,因为还未开始下棋,所以需用一个函数将棋盘的每个元素初始化为空格
	InitBoard(Board, ROW, COL);
	
	// 接着定义一个函数来打印棋盘
	DisplayBoard(Board, ROW, COL);

	// 玩家与电脑开始对战
	// 利用while循环,胜负未分时循环继续,胜负已分时结束循环
	// 同时,在while循环中,我们用一个iswin函数和if语句来判断游戏的输赢
	
	char ret = 0;
	while (1)
	{
		// 我们利用一个函数让玩家先手下棋
		PlayerMove(Board, ROW, COL);
		// 在玩家下棋成功后,打印棋盘。
		DisplayBoard(Board, ROW, COL);
		// 用ret函数接受函数的返回值,根据返回值来判断对战情况
		ret = IsWin(Board, ROW, COL);
		// 此处用if语句,除了ret为'C'时,其他情况都是游戏结束,跳出循环
		if (ret != 'C')
		{
			break;
		}
		
		// 在玩家下棋后让电脑下棋
		ComputerMove(Board, ROW, COL);
		// 在电脑下棋成功后,打印棋盘。
		DisplayBoard(Board, ROW, COL);
		// 用ret接受函数的返回值,根据返回值来判断对战情况
		ret = IsWin(Board, ROW, COL);
		// 此处用if语句,除了ret为'C'时,其他情况都是游戏结束,跳出循环
		if (ret != 'C')
		{
			break;
		}
	}

	// 在while循环结束后,即对战结束后,判断玩家赢还是电脑赢,又或是平局
	switch (ret)
	{
	case 'X':
		printf("恭喜你,你赢了!\n");
		break;
	case 'O':
		printf("游戏结束,你输了。\n");
		break;
	case 'D':
		printf("平局\n");
		break;
	}
}

int main()
{
	// 定义一个用来做选择的数
	int input = 0;
	
	// 利用do while语句来作出选择
	do
	{
		// 打印菜单
		menu();
		// 输入0或1来作出选择,1表示开始游戏,0表示离开游戏
		scanf("%d", &input);
		// 进入你所作出的选择中
		switch (input)
		{
		case 1:
			// 进入游戏
			game();
			break;
		case 0:
			// 离开游戏
			break;
		default:
			// 如果玩家输入了其他数字,提醒玩家输入错误,要求玩家重新输入
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input != 1 && input != 0);

	return 0;
}

http://lihuaxi.xjx100.cn/news/98256.html

相关文章

数说故事车企数字化转型案例分享——社媒营销投放哪个平台最佳?

一方面是不断推陈出新的社媒平台&#xff0c;各大平台对用户流量正在上演前所未有的争夺战&#xff0c;算法、玩法天天“迭代”&#xff0c;套路永远追不上变化&#xff1b;另一方面是“扑朔迷离”的消费者需求&#xff0c;你永远不知道TA最愿意“下单”的原因是什么。 数说故…

Spring—拦截器和过滤器介绍

介绍 过滤器&#xff08;Filter&#xff09; 过滤器是Java Web中奖传入的request、response提前过滤掉一些信息或提前设置一些参数&#xff0c;然后再传入到Servlet或action进行业务逻辑处理。如过滤非法url、过滤非法字符等。过滤器依赖于servlet容器&#xff0c;可以对几乎…

【Java中23种面试常考的设计模式之模板模式(Template)---行为型模式】

【Java中23种面试常考的设计模式之模板模式(Template)—行为型模式】 知识回顾: 之前我们讲过的设计模式在这里呦: 【面试最常见的设计模式之单例模式】 【面试最常见的设计模式之工厂模式】 【Java中23种面试常考的设计模式之备忘录模式(Memento)—行为型模式】 【Java中23种…

【day11】最近公共祖先最大连续bit数

最近公共祖先__牛客网 求父节点&#xff1a;父节点 孩子节点/2 &#xff08;1&#xff09;输入a &#xff0c;b 如果ab&#xff0c;表示ab的公共祖先就是ab本身 &#xff08;2&#xff09;如果ab不相同&#xff0c;循环while哪个大&#xff0c;哪个除以2&#xff0c;直到相…

关于Linux系统之VM安装配置(每一个步骤都超级详细的哦!)

目录 Linux简介 VM虚拟机的介绍及安装 VM简介 VM安装&#xff08;详细步骤&#xff0c;根据图片一步一步进行安装&#xff09; 查看IP配置IP 换源 1&#xff09;备份Linux自带的EPEL源 2&#xff09;生成阿里源 3&#xff09; 清空缓存 4&#xff09; 重新生成缓存 Linux…

职业转型|经济低迷期转型产品管理职业的五大建议

当前的经济低迷使许多人开始重新评估当前的工作并重新考虑新的职业道路。产品管理是一个非常热门的职业发展方向&#xff0c;受到了求职者们广泛的关注&#xff0c;但于此同时&#xff0c;这也加剧了这个领域的竞争。产品管理的进入门槛本就并不低&#xff0c;在经济衰退期间&a…

[爬虫]4.数据解析及应用 之 bs4【爬取一部小说的文本】

回顾&#xff0c;上节课我们学了什么&#xff1f; 聚焦爬虫 数据解析方式分类&#xff1a;正则表达式&#xff1b;bs4模块&#xff1b;xpath模块 F12查看网页标签的html格式 正则表达式详细表示方法 正则匹配 import re listre.findall(pattern,string,flags) 创建文件夹 爬取和…

python 线程安全和锁

python 线程安全和锁 进程是计算机中最小的资源分配单位。 线程是计算机中能被cpu调度的最小单位。 一、什么是线程&#xff1f; 线程也叫轻量级进程&#xff0c;是操作系统能够进行调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程的实际运作单位。线程自…