Fork me on GitHub

C语言开发实战:使用EasyX在Visual Studio 2022中创建井字棋游戏

C语言开发实战:使用EasyX在Visual Studio 2022中创建井字棋游戏

在本文中,我们将学习如何使用Visual Studio 2022和EasyX图形库来开发一个简单的井字棋游戏。EasyX是一个开源的图形库,它简化了Windows平台下的图形编程,使得开发者能够更快速地创建图形界面。

运行效果

环境搭建

首先,确保你已经安装了Visual Studio 2022。接下来,你需要下载并安装EasyX图形库。

  1. 访问EasyX的官方网站或GitHub页面下载最新版本的EasyX。
  2. 将EasyX的库文件解压到你的项目目录中。
  3. 在Visual Studio中创建一个新的C++项目,并配置项目以包含EasyX的头文件和库文件。

游戏逻辑实现

初始化游戏板

1
2
3
4
5
char board_data[3][3] = {
{'-','-','-'},
{'-','-','-'},
{'-','-','-'},
};

这是一个3x3的字符数组,用来表示游戏板的状态。'-'表示空格,'O''X'表示玩家的棋子。

检查胜利条件

1
2
3
4
5
6
7
8
9
10
11
12
13
bool CheckWin(char c) {
// 检查所有可能的获胜行、列和对角线
// 如果找到连续三个相同的字符,返回true
if (board_data[0][0] == c && board_data[0][1] == c && board_data[0][2] == c) return true;
if (board_data[1][0] == c && board_data[1][1] == c && board_data[1][2] == c) return true;
if (board_data[2][0] == c && board_data[2][1] == c && board_data[2][2] == c) return true;
if (board_data[0][0] == c && board_data[1][0] == c && board_data[2][0] == c) return true;
if (board_data[0][1] == c && board_data[1][1] == c && board_data[2][1] == c) return true;
if (board_data[0][2] == c && board_data[1][2] == c && board_data[2][2] == c) return true;
if (board_data[0][0] == c && board_data[1][1] == c && board_data[2][2] == c) return true;
if (board_data[0][2] == c && board_data[1][1] == c && board_data[2][0] == c) return true;
return false;
}

这个函数会检查游戏板上是否有玩家获胜。

检查平局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool CheckDraw() {
// 检查游戏板上是否有空格
// 如果所有位置都被填满,返回true,表示平局
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
if (board_data[i][j] == '-') {
return false;
}
}
}
return true;
}

这个函数会检查游戏是否平局。

绘制游戏板

1
2
3
4
5
6
7
void DrawBoard() {
// 使用EasyX的绘图函数绘制游戏板的边框
line(0, 200, 600, 200);
line(0, 400, 600, 400);
line(200, 0, 200, 600);
line(400, 0, 400, 600);
}

这个函数负责绘制游戏板的边框。

绘制棋子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void DrawPiece() {
// 根据游戏板的状态,绘制'O'或'X'棋子
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
switch (board_data[i][j])
{
case 'O':
circle(200 * j + 100, 200 * i + 100, 100);
break;
case 'X':
line(200 * j, 200 * i, 200 * (j + 1), 200 * (i + 1));
line(200 * (j + 1), 200 * i, 200 * j, 200 * (i + 1));
break;
case '-':
break;
}
}
}
}

这个函数会根据游戏板的状态绘制棋子。

绘制提示文本

1
2
3
4
5
6
7
void DrawTipText() {
// 显示当前轮到哪一位玩家下棋
static TCHAR str[64];
_stprintf_s(str, _T("当前棋子类型为:%c"), current_piece);
settextcolor(RGB(225, 175, 45));
outtextxy(0, 0, str);
}

这个函数会在屏幕上显示提示信息,告诉玩家当前轮到谁下棋。

游戏主循环

1
2
3
4
5
6
7
8
9
10
11
int main() {
// 初始化图形界面
initgraph(600, 600);
// 游戏循环
while (true) {
// 处理鼠标点击事件
// 绘制游戏板和棋子
// 检查游戏结束条件
}
return 0;
}

游戏的主循环负责处理用户输入、更新游戏状态、绘制游戏界面,并检查游戏是否结束。

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include<graphics.h>
#include<stdlib.h>

char board_data[3][3] = {
{'-','-','-'},
{'-','-','-'},
{'-','-','-'},
};

char current_piece = 'O';

bool CheckWin(char c) {
if (board_data[0][0] == c && board_data[0][1] == c && board_data[0][2] == c) return true;
if (board_data[1][0] == c && board_data[1][1] == c && board_data[1][2] == c) return true;
if (board_data[2][0] == c && board_data[2][1] == c && board_data[2][2] == c) return true;
if (board_data[0][0] == c && board_data[1][0] == c && board_data[2][0] == c) return true;
if (board_data[0][1] == c && board_data[1][1] == c && board_data[2][1] == c) return true;
if (board_data[0][2] == c && board_data[1][2] == c && board_data[2][2] == c) return true;
if (board_data[0][0] == c && board_data[1][1] == c && board_data[2][2] == c) return true;
if (board_data[0][2] == c && board_data[1][1] == c && board_data[2][0] == c) return true;
return false;
}

bool CheckDraw() {
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
if (board_data[i][j] == '-') {
return false;
}
}
}
return true;
}

void DrawBoard() {
line(0, 200, 600, 200);
line(0, 400, 600, 400);
line(200, 0, 200, 600);
line(400, 0, 400, 600);
}

void DrawPiece() {
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
switch (board_data[i][j])
{
case 'O':
circle(200 * j + 100, 200 * i + 100, 100);
break;
case 'X':
line(200 * j, 200 * i, 200 * (j + 1), 200 * (i + 1));
line(200 * (j + 1), 200 * i, 200 * j, 200 * (i + 1));
break;
case '-':
break;
}
}
}
}

void DrawTipText() {
static TCHAR str[64];
_stprintf_s(str, _T("当前棋子类型为:%c"), current_piece);
settextcolor(RGB(225, 175, 45));
outtextxy(0, 0, str);
}

int main() {
initgraph(600, 600);
bool runing = true;
ExMessage msg;
BeginBatchDraw();
while (runing)
{
DWORD start_time = GetTickCount();
while (peekmessage(&msg))
{
if (msg.message == WM_LBUTTONDOWN)
{
int x = msg.x;
int y = msg.y;
int index_x = x / 200;
int index_y = y / 200;
if (board_data[index_y][index_x] == '-')
{
board_data[index_y][index_x] = current_piece;
if (current_piece == 'X')
{
current_piece = 'O';
}
else {
current_piece = 'X';
}
}
}
}
cleardevice();
DrawBoard();
DrawPiece();
DrawTipText();
FlushBatchDraw();

if (CheckWin('X'))
{
MessageBox(GetHWnd(), _T("玩家X获胜"), _T("游戏结束"), MB_OK);
runing = false;
}
else if (CheckWin('O'))
{
MessageBox(GetHWnd(), _T("玩家O获胜"), _T("游戏结束"), MB_OK);
runing = false;
}
else if (CheckDraw())
{
MessageBox(GetHWnd(), _T("平局"), _T("游戏结束"), MB_OK);
runing = false;
}
DWORD end_time = GetTickCount();
DWORD delta_time = end_time - start_time;
if (delta_time < 1000 / 60) {
Sleep(1000 / 60 - delta_time);
}
}
EndBatchDraw();
return 0;
}

结语

通过上述步骤,我们成功地使用Visual Studio 2022和EasyX开发了一个简单的井字棋游戏。这个教程展示了如何设置游戏环境、实现游戏逻辑、处理用户输入以及绘制游戏界面。现在,你可以在此基础上添加更多功能,如AI对手、网络对战等,来丰富你的游戏。

前端与后端协同:实现Excel导入导出功能

前端与后端协同:实现Excel导入导出功能

在现代Web应用中,Excel文件的导入导出是一个常见的需求。用户希望能够方便地将数据导入到系统中,或者将系统数据导出到Excel文件中。本文将介绍如何在前端和后端之间实现这一功能,示例采用Spring Boot和Vue.js技术栈。

后端依赖

在Spring Boot项目中,我们需要添加EasyExcel依赖来处理Excel文件。EasyExcel是一个简单、省内存的读写Excel的开源工具。

1
2
3
4
5
6
<!-- 导出excel  -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>

后端代码

导出Excel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//@Operation(summary = "导出数据") swagger注解
@PostMapping("exportExcel")
public void exportExcelMenu(HttpServletResponse response) throws IOException {
// 设置响应头信息
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 编码文件名,避免中文乱码
String fileName = URLEncoder.encode("轮播图", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
// 获取数据列表
List<MenuEntity> list = menuService.list();
// 设置文件名
response.setHeader("Content-disposition", "attachment;filename*=" + fileName + ".xlsx");
// 使用EasyExcel写入数据到输出流
EasyExcel.write(response.getOutputStream(), MenuEntity.class)
.sheet("轮播图") // 指定工作表名称
.doWrite(list); // 执行写入操作
}

导入Excel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//@Operation(summary = "导入数据") swagger注解
@PostMapping("/importExcel")
public Result importExcelMenu(MultipartFile file) {
try {
// 获取文件输入流
InputStream inputStream = file.getInputStream();
// 使用EasyExcel读取Excel数据
List<MenuEntity> list = EasyExcel.read(inputStream, MenuEntity.class)
.sheet() // 读取第一个工作表
.headRowNumber(1) // 表头行数
.doReadSync(); // 同步读取数据
// 处理导入的数据
for (MenuEntity entity : list) {
menuService.saveOrUpdate(entity);
}
// 返回成功结果
return Result.success("导入成功", sdf.format(new Date()));
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}

前端请求

导入数据

1
2
3
export function importExcelMenu(file: object): Promise<ImportExcelMenuRes> {
return request.post(`/menuEntity/importExcel`, file);
}

导出数据

1
2
3
4
5
6
export function exportExcelMenu(): Promise<ExportExcelMenuRes> {
return request.post(`/menuEntity/exportExcel`, {}, {
responseType: 'arraybuffer', // 设置响应类型为二进制流
headers: {'Content-Type': 'application/octet-stream'} // 设置请求头
});
}

前端调用请求

导入操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const importChangeExcel = (file) => {
// 限制文件类型
if (file.raw.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
ElMessage.error('仅支持 .xlsx 格式的文件');
return false;
}
importData.value.append('file', file.raw);
};

const GlobalImport = () => {
// 执行导入操作
api.menu.importExcelMenu(importData.value).then((res: any) => {
ElMessage({message: res.message, type: res.success ? 'success' : 'error'});
});
};

导出操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const GlobalExport = () => {
// 执行导出操作
api.menu.exportExcelMenu().then((res: any) => {
// 创建下载链接并触发下载
const link = document.createElement('a');
const fileName = "菜单表.xlsx";
let blob = new Blob([res], {type: 'application/vnd.ms-excel'});
link.href = URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};

注意

  1. 导入数据的接口中,相当于使用@RequestBody注解获取数据:

    1
    2
    3
    export function importExcelMenu(file: object): Promise<ImportExcelMenuRes> {
    return request.post(`/menuEntity/importExcel`, file);
    }
  2. 导出数据的接口中,需要设置如下请求头:

    1
    2
    3
    4
    {
    responseType: 'arraybuffer', // 设置响应类型为二进制流
    headers: {'Content-Type': 'application/octet-stream'} // 设置请求头
    }

结语

通过上述示例,我们展示了如何在前端和后端之间实现Excel文件的导入导出功能。后端通过Spring Boot和EasyExcel处理Excel文件,前端通过Vue.js发起请求并处理响应。这样的协同工作使得数据的导入导出变得简单而高效。开发者可以根据实际需求调整和扩展这些代码,以适应不同的业务场景。

Java日期格式化:掌握时间的艺术

Java日期格式化:掌握时间的艺术

Java中的new Date()

当你创建一个新的Date对象时,它默认不会以任何特定的格式显示。如果你直接打印一个Date对象,会得到类似下面的输出,这是一个由java.lang.Long对象提供的字符串表示:

1
2
3
Date date = new Date();
System.out.println(date);
// 输出类似于:Fri Mar 28 14:30:00 UTC 2024

这个输出是一个简单的文本表示,显示了日期和时间的大致信息,包括星期几、月份、日期、小时、分钟和秒,以及时区信息。但这不是格式化后的日期,而是一个Date对象的toString()方法的默认输出。

Java日期格式化

在Java编程中,日期和时间的处理是一项基本而重要的任务。Java提供了java.text.SimpleDateFormat类和Java 8引入的java.time.format.DateTimeFormatter类来格式化日期和时间。本文将介绍如何在Java中格式化日期,并提供相应的代码示例。

使用SimpleDateFormat格式化日期

SimpleDateFormat是Java日期格式化的传统方法。它使用模式字符串来定义日期和时间的格式。

1
2
3
4
5
6
7
8
9
10
11
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatExample {
public static void main(String[] args) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String formattedDate = formatter.format(date);
System.out.println("Formatted date: " + formattedDate);
}
}

在这个例子中,SimpleDateFormat对象使用"yyyy-MM-dd HH:mm:ss"模式来格式化当前日期和时间。yyyy代表年,MM代表月,dd代表日,HH代表24小时制的小时,mm代表分钟,ss代表秒。

使用DateTimeFormatter格式化日期

Java 8引入了新的日期和时间API,其中DateTimeFormatter类提供了更强大的日期格式化功能。

1
2
3
4
5
6
7
8
9
10
11
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterExample {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = dateTime.format(formatter);
System.out.println("Formatted date and time: " + formattedDateTime);
}
}

在这个例子中,我们首先获取当前的日期和时间作为一个LocalDateTime对象,然后使用DateTimeFormatter来格式化它。ofPattern方法接受一个模式字符串,与SimpleDateFormat的用法类似。

常见日期格式

  • "yyyy-MM-dd":年-月-日,例如 2024-03-28
  • "HH:mm:ss":24小时制的小时:分钟:秒,例如 14:30:00
  • "yyyy-MM-dd HH:mm:ss":年-月-日 时:分:秒,例如 2024-03-28 14:30:00
  • "EEE, MMM d, ''yy":星期几, 月份缩写 d, 年的后两位,例如 Fri, Mar 28, '24

结语

日期格式化是Java中处理日期和时间的基础。无论是使用SimpleDateFormat还是DateTimeFormatter,都能够满足大多数格式化需求。了解和掌握这些工具,将帮助你在Java程序中有效地处理和展示日期和时间信息。

正则表达式与JSON序列化:去除JavaScript对象中的下划线键名

正则表达式与JSON序列化:去除JavaScript对象中的下划线键名

在处理前端数据时,我们经常会遇到一些以下划线开头的属性,这在某些情况下可能会与JSON标准或其他数据格式的要求冲突。为了确保数据的一致性和兼容性,我们需要将这些属性的下划线去除。本文将介绍如何使用JSON.stringify()进行序列化,并通过replace()方法结合正则表达式来实现这一转换。

JSON.stringify()简介

JSON.stringify()是一个内置的JavaScript函数,用于将JavaScript对象转换成JSON字符串。它可以处理各种数据类型,包括对象、数组、字符串、数字等。然而,JSON.stringify()默认不会序列化以下划线开头的属性,因为这些属性在JSON中不是有效的键名。

使用replace()和正则表达式

为了解决这个问题,我们可以使用replace()方法,它允许我们对字符串进行搜索和替换。结合正则表达式,我们可以精确地匹配并替换字符串中的特定模式。

正则表达式基础

正则表达式是一种强大的文本处理工具,它定义了一个搜索模式,用于在字符串中查找和操作符合某种模式的文本。例如,/_(\w+)/g是一个正则表达式,它匹配以下划线开头的任何单词字符序列。

  • /:定界符,用来标记正则表达式的开始和结束。
  • _:字面意义上的下划线字符。
  • (\w+):匹配一个或多个字母、数字或下划线,圆括号表示捕获组。
  • g:全局搜索标志,表示查找所有匹配项,而不是停在第一个匹配项。

实现下划线去除

现在我们可以结合JSON.stringify()replace()方法来序列化对象,并去除属性名中的下划线:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const basicForm = {
_id: null,
_name: '',
_phone: '',
_email: '',
_sex: '',
_age: '',
};

// 使用JSON.stringify()序列化对象
const jsonString = JSON.stringify(basicForm);

// 使用正则表达式替换属性名中的下划线
const cleanedJson = jsonString.replace(/_(\w+)/g, (match, p1) => {
return p1; // 这里直接返回捕获的单词字符序列,即去除下划线
});

console.log(cleanedJson);

// 使用JSON.stringify()序列化对象
const jsonString = JSON.stringify(basicForm);

// 使用正则表达式替换属性名中的下划线
const cleanedJson = jsonString.replace(/_(\w+)/g, (match, p1) => {
return p1; // 这里直接返回捕获的单词字符序列,即去除下划线
});

console.log(cleanedJson);
//打印结果
//{"id":null,"name":"","phone":"","email":"","sex":"","age":""}
//可以使用JSON.parse(cleanedJson)包装为一个对象

在这个例子中,replace()方法使用正则表达式/_(\w+)/g来查找所有以下划线开头的属性名,并通过第二个参数函数将下划线去除。

结语

通过结合JSON.stringify()和正则表达式,我们能够有效地处理JavaScript对象的序列化问题,并确保数据格式的一致性。这种方法不仅适用于去除下划线,还可以用于各种复杂的字符串处理任务。掌握正则表达式的使用,将极大地提升你在文本处理方面的能力。

探索async/await的魔力:简化JavaScript异步编程

探索async/await的魔力:简化JavaScript异步编程

在JavaScript的世界里,异步编程一直是开发者必须面对的挑战。传统的异步模式,如回调函数、Promise链和链式调用,虽然功能强大,但有时也会使代码变得复杂难懂。幸运的是,asyncawait关键字的出现,为我们提供了一种更加直观和简洁的方式来处理异步操作。

async/await的简介

asyncawait是JavaScript ES2017引入的两个新关键字,它们使得异步代码的编写更加接近同步代码的结构,提高了代码的可读性和可维护性。

  • async关键字用于声明一个函数是异步的,它可以在函数定义前使用。async函数内部返回的值会被自动包装成一个Promise。
  • await关键字用于等待一个Promise完成(resolve)或拒绝(reject),它可以暂停函数的执行,直到Promise的结果可用。

如何使用async/await

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
}

在这个例子中,fetchData函数被标记为async,这意味着它可以内部使用awaitawait关键字用于等待网络请求完成并将响应转换为JSON格式。如果在这个过程中发生错误,它会被catch块捕获。

并发执行多个异步操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function getAllData() {
try {
const [user, post] = await Promise.all([
fetch('/api/user'),
fetch('/api/post')
]);

const [userData, postData] = await Promise.all([
user.json(),
post.json()
]);

return { user: userData, post: postData };
} catch (error) {
console.error('Error fetching data:', error);
}
}

在这个例子中,getAllData函数使用Promise.all来并发执行两个网络请求,并等待它们都完成。然后,它再次使用Promise.all来并发地将两个响应转换为JSON格式。这种方式使得并发执行异步操作变得非常简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fetchData = async () => {
try {
// 这里可以使用 await 来等待一个 Promise
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log(data);
// 你可以继续使用 await 来等待其他的异步操作
} catch (error) {
// 如果有错误发生,它会被捕获在这里
console.error('There was a problem with the fetch operation:', error);
}
};

在这个例子中,fetchData函数被声明为async,这意味着你可以在函数内部使用await。我们使用await fetch来等待网络请求完成,然后使用await response.json()来等待JSON数据的解析。如果在这个过程中发生任何错误,它们会被catch块捕获并处理。

注意事项

  • async函数内部的代码执行顺序并不会改变,await只是暂停函数的进一步执行,而不是暂停JavaScript事件循环。
  • await可以与任何返回Promise对象的异步操作一起使用,包括自定义的异步函数。
  • async函数中使用return语句时,返回的值会被Promise对象包装。如果返回的是一个非Promise对象,它会被立即解决(resolved)。

结语

asyncawait为我们提供了一种更加简洁和直观的方式来编写异步代码。它们不仅使代码更容易阅读和理解,还减少了回调地狱和复杂链式调用带来的困扰。随着JavaScript异步编程的不断发展,asyncawait已经成为了现代JavaScript开发中不可或缺的工具。掌握它们,将使你的代码更加健壮和高效。

Windows搭建C语言和EasyX开发环境

Windows搭建C语言和EasyX开发环境

安装Visual Studio 2022

Visual Studio 2022是微软推出的最新集成开发环境(IDE),适用于Windows应用程序和游戏的开发。

  • 官网下载地址

安装EasyX

EasyX是一个开源的图形库,用于简化Windows平台下的图形编程。

  • 官网下载地址

    • 访问EasyX的官方网站 https://easyx.cn/ 并下载最新版本的EasyX库。
    • 解压下载的文件到一个目录。
    • image-20240401120254703
    • image-20240401120325475
  • 官方文档

配置EasyX(可选)

  1. 打开Visual Studio,创建一个新的C++项目。
  2. 右键点击项目,选择“属性”。
  3. 在“配置”选项卡中,设置“包含目录”(Include Directories)和“库目录”(Library Directories),指向EasyX的includelib文件夹。
  4. 在“链接器”选项卡中,添加EasyX的.lib文件。
  5. image-20240401120954398

编写测试代码

以下是一个使用EasyX的简单示例代码,用于创建一个窗口并在其中绘制一个矩形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <graphics.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
initgraph(640, 480); // 初始化图形窗口大小为640x480
setbkcolor(WHITE); // 设置背景颜色为白色
cleardevice(); // 清屏
setcolor(BLACK); // 设置画笔颜色为黑色
rectangle(100, 100, 200, 200); // 画一个矩形
system("pause"); // 等待
closegraph(); // 关闭图形窗口
return 0;
}
  1. 将上述代码复制到Visual Studio的C++项目中。
  2. 确保项目配置正确,然后编译并运行程序。
  3. 如果一切设置正确,你将看到一个640x480的窗口,并在窗口中显示了一个矩形。
  4. image-20240401120837287

结语

通过上述步骤,你应该能够在Windows系统上成功搭建EasyX开发环境。如果在安装或配置过程中遇到问题,可以参考EasyX的官方文档或在相关开发者社区寻求帮助。现在,你可以开始使用EasyX进行图形编程或游戏开发了!

JavaScript日期格式化:从原始值到用户友好的字符串

JavaScript日期格式化:从原始值到用户友好的字符串

在JavaScript中,日期和时间的处理是开发中常见的需求之一。为了在用户界面上显示日期和时间,或者在日志记录中保持一致的格式,我们需要将JavaScript的日期对象格式化为可读的字符串。本文将介绍JavaScript中的日期处理,特别是Date对象的使用,以及如何编写一个函数来格式化日期。

JavaScript中的Date对象

JavaScript的Date对象是一个内置对象,用于表示特定的日期和时间。它提供了多种方法来获取和设置日期和时间的各个部分,如年、月、日、小时、分钟和秒。

创建Date对象

你可以通过多种方式创建一个Date对象:

1
2
3
4
5
// 使用当前日期和时间创建Date对象
let now = new Date();

// 使用指定的年、月、日、时、分、秒创建Date对象
let specificDate = new Date(2024, 3, 28, 14, 30, 0);

获取日期和时间的组成部分

Date对象提供了如下方法来获取日期的各个部分:

1
2
3
4
5
6
7
let date = new Date();
console.log(date.getFullYear()); // 获取年份
console.log(date.getMonth() + 1); // 获取月份(注意月份从0开始)
console.log(date.getDate()); // 获取日
console.log(date.getHours()); // 获取小时
console.log(date.getMinutes()); // 获取分钟
console.log(date.getSeconds()); // 获取秒

格式化日期

在实际应用中,我们通常需要将Date对象格式化为特定的字符串格式。下面是一个formatDateTime函数的示例,它接受一个日期字符串或Date对象,并返回格式化后的日期字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export const formatDateTime = (date) => {
if (date === "" || !date) {
return "";
}
var d = new Date(date);
var y = d.getFullYear();
var m = d.getMonth() + 1;
var d = d.getDate();
var h = d.getHours();
var minute = d.getMinutes();
var second = d.getSeconds();

m = m < 10 ? ('0' + m) : m;
d = d < 10 ? ('0' + d) : d;
h = h < 10 ? ('0' + h) : h;
minute = minute < 10 ? ('0' + minute) : minute;
second = second < 10 ? ('0' + second) : second;

return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second;
}

使用示例

假设我们有一个Date对象,我们想要将其格式化为YYYY-MM-DD HH:MM:SS的格式:

1
2
let now = new Date();
console.log(formatDateTime(now)); // 输出:2024-03-28 14:30:00

或者我们有一个日期字符串:

1
2
let dateString = "2024-03-28T14:30:00.000Z";
console.log(formatDateTime(dateString)); // 输出:2024-03-28 14:30:00

通过上述formatDateTime函数,我们可以轻松地将JavaScript的日期对象转换为用户友好的格式化字符串。这在开发Web应用程序时非常有用,尤其是在需要显示日期和时间信息的场合。

Spring Boot单元测试全指南:使用Mockito和AssertJ

Spring Boot单元测试全指南:使用Mockito和AssertJ

在现代软件开发实践中,单元测试是不可或缺的一环,它帮助我们确保代码的可靠性和稳定性。对于使用Spring Boot构建的应用,编写单元测试不仅可以验证业务逻辑的正确性,还可以确保服务的健壮性。本文将详细介绍如何在Spring Boot项目中进行单元测试,包括使用Mockito进行依赖模拟和使用AssertJ进行断言。

1. 添加测试依赖

在开始编写测试之前,我们需要确保项目中包含了Spring Boot的测试依赖。这些依赖包括JUnit(测试框架)、Mockito(模拟框架)和AssertJ(断言库)。以下是Maven的依赖配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<dependencies>
<!-- Spring Boot Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- JUnit 5 dependency -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito dependency -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<!-- AssertJ dependency -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2. 编写服务类

假设我们有一个MyService服务类,它处理一些业务逻辑,并依赖于AnotherService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class MyService {
private final AnotherService anotherService;

public MyService(AnotherService anotherService) {
this.anotherService = anotherService;
}

public String doSomething(String input) {
// 假设这个方法处理输入,并依赖anotherService
String processedInput = anotherService.processInput(input);
return "Result: " + processedInput;
}
}

3. 创建测试类

创建一个名为MyServiceTest的测试类,并使用@SpringBootTest注解来标记这是一个Spring Boot的测试类。这将启动Spring应用上下文,允许我们注入真正的Spring Beans。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.example.service;

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MyServiceTest {

@Mock
private AnotherService anotherService;

@InjectMocks
private MyService myService;

@Test
void testDoSomething() {
// Arrange
String input = "test";
String expectedOutput = "Result: test processed";
String processedInput = "test processed";
when(anotherService.processInput(input)).thenReturn(processedInput);

// Act
String result = myService.doSomething(input);

// Assert
assertThat(result).isEqualTo(expectedOutput);
}

// 初始化Mockito注解
@Test
void init() {
MockitoAnnotations.openMocks(this);
}
}

MyServiceTest类中,我们使用@Mock注解创建了一个AnotherService的模拟对象,并通过@InjectMocks注解将其注入到MyService中。在testDoSomething测试方法中,我们模拟了processInput方法的返回值,并调用了doSomething方法来验证结果是否符合预期。

4. 运行测试

运行测试方法,可以使用IDE的测试运行功能,或者使用Maven/Gradle命令行工具。例如,在Maven项目中,你可以使用以下命令来运行测试:

1
mvn test

5. 结语

通过上述步骤,我们可以在Spring Boot项目中编写和运行单元测试。使用Mockito和AssertJ,我们可以方便地模拟依赖和验证结果,确保代码的正确性和稳定性。记住,良好的测试习惯是持续交付高质量软件的关键。通过编写单元测试,我们可以在代码变更时快速发现问题,提高开发效率,同时也为未来的代码维护打下坚实的基础。

CentOS系统下Docker的安装教程

CentOS系统下Docker的安装教程

Docker是一个开源的应用容器引擎,它允许开发人员将应用及其依赖打包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器或Windows机器上,也可以实现虚拟化。在本文中,我们将探讨如何在CentOS系统上安装Docker。

支持的CentOS版本

Docker支持以下64位CentOS版本:

  • CentOS 7
  • CentOS 8
  • 更高版本

使用官方安装脚本自动安装

自动安装是最简单的安装方式,只需运行以下命令即可:

1
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

这条命令会从Docker的官方网站下载安装脚本,并自动执行安装过程。其中--mirror Aliyun表示使用阿里云的镜像加速,这对于国内用户来说可以大大提高下载速度。

手动安装

如果你希望手动控制安装过程,可以按照以下步骤进行:

卸载旧版本

如果你的系统中已经安装了旧版本的Docker(称为docker或docker-engine),请先卸载它们:

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装Docker Engine-Community

设置仓库

在安装Docker Engine-Community之前,需要先设置Docker仓库。你可以使用官方源,也可以选择国内的镜像源,如阿里云或清华大学源。

安装必要的软件包:

1
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

使用以下命令设置稳定的仓库:

1
2
3
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

或者选择国内的镜像源:

1
2
3
4
5
6
7
8
sudo yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 或者
sudo yum-config-manager \
--add-repo \
https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo

安装Docker Engine-Community

安装最新版本的Docker Engine-Community和containerd:

1
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

如果你需要安装特定版本的Docker,可以通过以下步骤操作:

  1. 列出并排序你仓库中可用的版本:
1
yum list docker-ce --showduplicates | sort -r
  1. 通过其完整的软件包名称安装特定版本:
1
sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

启动Docker

安装完成后,启动Docker服务:

1
sudo systemctl start docker

验证安装

通过运行hello-world镜像来验证是否正确安装了Docker Engine-Community:

1
sudo docker run hello-world

如果看到“Hello from Docker!”这样的输出,那么恭喜你,Docker已经成功安装并可以正常工作了。

卸载Docker

如果你需要卸载Docker,可以执行以下命令:

1
yum remove docker-ce

删除镜像、容器、配置文件等内容:

1
rm -rf /var/lib/docker

结语

以上就是在CentOS系统上安装Docker的详细步骤。无论你选择自动安装还是手动安装,都可以根据这些步骤来操作。安装Docker后,你将能够轻松地在容器中运行和部署应用,提高开发和部署的效率。

Yarn简介及Windows安装与使用指南

Yarn简介及Windows安装与使用指南

在现代的Web开发中,JavaScript项目的依赖管理是一个复杂而重要的任务。幸运的是,我们有多种工具可以帮助我们处理这些依赖,其中之一就是Yarn。Yarn是一个由Facebook、Google、Tilde和Exponent联合开发的跨平台包管理工具,它旨在提供更快、更可靠的依赖管理体验。本文将为你介绍Yarn的基本概念,以及如何在Windows系统上通过Node.js使用npm安装Yarn,并介绍Yarn的基本使用。

什么是Yarn?

Yarn是一个快速、可靠、安全的依赖管理工具,它允许开发者在不同环境中一致地管理和安装项目依赖。Yarn通过锁定依赖的确切版本来确保项目的稳定性,并提供了并行安装依赖的能力,从而大大提高了安装速度。Yarn还具有优秀的缓存机制,可以重用之前下载的包,进一步加快了依赖安装过程。

为什么选择Yarn?

选择Yarn的理由有很多,以下是一些主要的优点:

  • 性能:Yarn通过并行处理和缓存机制,使得依赖安装速度更快。
  • 一致性:Yarn的锁定机制确保了每次安装的依赖版本完全一致,有助于避免因版本差异导致的问题。
  • 安全性:Yarn在安装依赖时会检查依赖的完整性,确保了项目的安全性。
  • 兼容性:Yarn支持多种平台,包括Windows、macOS和Linux。

在Windows上安装Yarn

在Windows系统上安装Yarn可以通过Node.js的包管理器npm来完成。以下是详细步骤:

步骤 1:安装Node.js

  1. 访问Node.js的官方下载页面:https://nodejs.org/
  2. 下载适用于Windows的安装程序。
  3. 运行安装程序并按照提示完成安装。在安装过程中,确保选择了npm包管理器的安装选项。

步骤 2:使用npm安装Yarn

安装Node.js后,打开命令提示符(CMD)或PowerShell,输入以下命令来全局安装Yarn:

1
npm install --global yarn

步骤 3:验证安装

安装完成后,输入以下命令来验证Yarn是否已成功安装:

1
yarn --version

如果安装成功,命令行将显示Yarn的版本信息。

Yarn的安装和使用

安装Yarn后,你可以开始使用它来管理你的JavaScript项目了。以下是一些基本的Yarn命令:

初始化新项目

在你的项目目录中,运行以下命令来创建一个新的 package.json 文件:

1
yarn init

这个命令会引导你通过一系列问题来设置你的项目,包括名称、版本、描述、入口点等。

安装依赖

使用 Yarn 安装一个依赖到你的项目中,运行:

1
yarn add [package-name]

如果你想安装一个开发依赖(只在开发环境中使用),可以使用:

1
yarn add [package-name] --dev

升级依赖

要升级一个已安装的依赖,可以使用以下命令:

1
yarn upgrade [package-name]

移除依赖

要移除一个依赖,运行:

1
yarn remove [package-name]

运行脚本

Yarn 允许你通过 package.json 中定义的脚本来运行任务。例如,如果你想运行一个名为 start 的脚本,可以使用:

1
yarn start

检查依赖冲突

Yarn 可以帮助你检查项目中的依赖冲突:

1
yarn dedupe

这个命令会尝试解决依赖树中的冲突。

结语

Yarn是一个强大的依赖管理工具,它可以帮助你更高效地管理项目的依赖。通过上述步骤,你可以轻松地在Windows系统上通过Node.js的npm安装并开始使用Yarn。随着你对Yarn的进一步了解和使用,你会发现它在提高开发效率和项目管理方面的巨大价值。不要忘记查看Yarn的官方文档来获取更多高级功能和详细信息。

  • Copyrights © 2022-2024 何福海
  • 访问人数: | 浏览次数:

请我喝杯奶茶吧~

支付宝
微信