改进strtok是不可以嵌套使用的问题

本文探讨了strtok函数在嵌套使用时遇到的问题,并提供了一种解决方案。strtok函数在字符串分割过程中会改变原始字符串,当在一个函数内嵌套调用时可能会导致上层函数的分割操作失效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

strtok是不可以嵌套使用的!

刚才写程序的时候,在while循环里面调用了另一个函数,里面嵌套了strtok,总是得不到预想的结果,(调试了半天,才最后把焦点落到了C库里的strtok函数)。

这算是strtok的一个bug;

因为在while循环里面的strtok返回的值为NULL,改变了上层strtok函数的全局变量值,不能再继续默认的使用strtok(NULL,“”);这一写法,可能需要我们个人手动改进上层循环的NULL值,记录下strtok分隔后的位移值,然后再次分隔时,把NULL值改成上次分隔后的首地址,继续向后搜索分隔;


alpha_search_word()函数里嵌套了另一个strtok函数 ,因为strtok分隔后只是在第一个找到的分隔符替换成'\0',故可以直接找到剩下句子的首地址,char *index_2=index+strlen(index)+1;程序运行过后,完美支持。

这是个人的一个想法,其他朋友有更好的办法,期待分享。





#define _CRT_SECURE_NO_WARNINGS #pragma comment(lib, "winmm.lib") #pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup") #include <graphics.h> // EasyX图形库头文件 #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <mmsystem.h> // 用于播放音频 #include <time.h> // 宏定义 #define true 1 #define false 0 #define bool int #define MAX_SIZE 7 // 最大拼图规模 #define N 9 // 拼图块种类数 #define ROWS 3 // 行数 #define COLS 3 // 列数 // 全局变量 int WINDOW_WIDTH = 1000; // 窗口宽度 int WINDOW_HEIGHT = 700; // 窗口高度 int BUTTON_X = 300; // 按钮X坐标 int BUTTON_Y = 200; // 按钮Y坐标 int BUTTON_WIDTH = 200; // 按钮宽度 int BUTTON_HEIGHT = 50; // 按钮高度 int degrees[] = { 3,4,5,6,7 }; // 难度级别对应的拼图尺寸 char str[N][1000]; // 存储传统文化描述的数组 int key = 0; // 全局键值变量 ExMessage msg{ 0 }; // 消息结构体变量 // 可选图片结构体 struct Pictures { const char* name; // 图片名称 int num; // 图片编号 }; // 图片资源数组 struct Pictures picture[] = { {"京剧脸谱",1}, {"中国结",2}, {"剪纸",3}, {"书法",4}, {"国画",5}, {"陶瓷",6}, {"刺绣",7}, {"皮影",8}, {"灯笼",9} }; // 拼图游戏结构体 struct PuzzleGame { int size; // 拼图规模 (3x3, 4x4等) int board[MAX_SIZE][MAX_SIZE]; // 拼图数据 int emptyRow, emptyCol; // 空白格位置 int moves; // 移动步数 int picIndex; // 选择的图片索引 }; /* 函数声明 */ void GetImage(const char* filename, int lx, int ly); void cut_image(const char* filename); int degree_charToInt(); void Button(int, int, const char* word); void Menu(); void ReadData(); void WriteSingleSelectedData(int i); void WriteAllData(); bool isClick(int x, int y); void PuzzlePage(int degree, int picIndex); int ModeSelection(); int Selectpicture(); void Information(int key); void InitPuzzle(struct PuzzleGame* game, int size, int picIndex); bool CheckWin(struct PuzzleGame* game); bool MoveTile(struct PuzzleGame* game, int direction); void DrawPuzzle(struct PuzzleGame* game); void ShowWinScreen(struct PuzzleGame* game); /************************** * 主函数 **************************/ int main() { // 初始化随机数种子 srand((unsigned)time(NULL)); // 读取传统文化描述数据 ReadData(); // 进入主菜单循环 while (true) { Menu(); } closegraph(); return 0; } /************************** * 数据读取函数 * 从文件读取传统文化描述 **************************/ void ReadData() { FILE* file = fopen("data.txt", "r"); if (file == NULL) { // 文件打开失败时使用默认文本 for (int i = 0; i < N; i++) { sprintf(str[i], "元素 %d 描述加载失败", i + 1); } return; } // 逐行读取描述 for (int i = 0; i < N; i++) { if (fgets(str[i], sizeof(str[i]), file) != NULL) { // 去除换行符 size_t len = strlen(str[i]); if (len > 0 && str[i][len - 1] == '\n') { str[i][len - 1] = '\0'; } } } fclose(file); } /************************** * 显示单个传统文化描述 **************************/ void WriteSingleSelectedData(int i) { outtextxy(0, 0, str[i]); } /************************** * 显示所有传统文化描述 **************************/ void WriteAllData() { for (int i = 0; i < N; i++) { outtextxy(i, i * 5, str[i]); } } /************************** * 加载并显示图片 * @param filename 图片路径 * @param lx,ly 显示位置坐标 **************************/ void GetImage(const char* filename, int lx, int ly) { IMAGE image; loadimage(&image, filename); putimage(lx, ly, &image); } /************************** * 切割图片函数(备用) **************************/ void cut_image(const char* filename) { IMAGE img; loadimage(&img, filename); int width = img.getwidth(); int height = img.getheight(); int blockWidth = width / COLS; int blockHeight = height / ROWS; IMAGE blocks[ROWS][COLS]; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { blocks[i][j] = IMAGE(blockWidth, blockHeight); getimage(&blocks[i][j], j * blockWidth, i * blockHeight, blockWidth, blockHeight); } } putimage(0, 0, &blocks[0][0]); } /************************** * 改进后的按钮点击检测函数 * @param x,y 按钮位置偏移量 * @return 是否点击了按钮 **************************/ bool isClick(int x, int y) { // 检查鼠标消息队列中的所有消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查是否在按钮区域内 bool inArea = (msg.x >= BUTTON_X + x && msg.y >= BUTTON_Y + y && msg.x <= BUTTON_X + x + BUTTON_WIDTH && msg.y <= BUTTON_Y + y + BUTTON_HEIGHT); // 移除已处理的消息 while (peekmessage(&msg, EX_MOUSE, PM_REMOVE)); return inArea; } // 移除其他鼠标消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } return false; } /************************** * 绘制按钮函数 * @param x,y 按钮位置偏移量 * @param word 按钮文字 **************************/ void Button(int x, int y, const char* word) { // 设置按钮样式 setbkmode(TRANSPARENT); setfillcolor(RGB(231, 250, 235)); solidroundrect(BUTTON_X + x, BUTTON_Y + y, BUTTON_X + x + BUTTON_WIDTH, BUTTON_Y + y + BUTTON_HEIGHT, 20, 20); // 设置文字样式 settextstyle(50, 15, "华文宋体"); setbkmode(TRANSPARENT); settextcolor(RGB(35, 30, 30)); // 计算文字居中位置 int w = (BUTTON_WIDTH - textwidth(word)) / 2; int h = (BUTTON_HEIGHT - textheight(word)) / 2; outtextxy(BUTTON_X + x + w, BUTTON_Y + y + h, word); } /************************** * 改进后的模式选择界面 * @return 选择的拼图尺寸 **************************/ int ModeSelection() { PlaySound(NULL, NULL, 0); PlaySound("dusk.wav", NULL, SND_ALIAS | SND_FILENAME | SND_ASYNC); cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 五种难度按钮 const char* str[] = { "简单模式","初级模式","中级模式","高级模式","终极模式" }; for (int i = 0; i < 5; i++) { Button(0, i * 100, str[i]); } // 显示提示信息 settextstyle(20, 10, "华文宋体"); outtextxy(50, 550, "请点击选择游戏难度"); // 等待选择 while (true) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查点击了哪个按钮 for (int i = 0; i < 5; i++) { int buttonTop = BUTTON_Y + i * 100; int buttonBottom = buttonTop + BUTTON_HEIGHT; if (msg.x >= BUTTON_X && msg.x <= BUTTON_X + BUTTON_WIDTH && msg.y >= buttonTop && msg.y <= buttonBottom) { return degrees[i]; } } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } } /************************** * 图片选择界面 * @return 选择的图片索引 **************************/ int Selectpicture() { cleardevice(); PlaySound(NULL, NULL, 0); PlaySound("main.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP); const int startX = 0, startY = 10; int selected = -1; // 3x3网格显示9种传统文化图片选项 for (int i = 0; i < 9; i++) { int row = i / 3, col = i % 3; Button(startX + col * 200, startY + row * 150, picture[i].name); } // 显示提示信息 settextstyle(20, 10, "华文宋体"); outtextxy(50, 550, "请点击选择拼图图片"); // 等待选择 while (selected == -1) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查点击了哪个图片按钮 for (int i = 0; i < 9; i++) { int row = i / 3, col = i % 3; int buttonX = BUTTON_X + col * 200; int buttonY = BUTTON_Y + row * 150; if (msg.x >= buttonX && msg.x <= buttonX + BUTTON_WIDTH && msg.y >= buttonY && msg.y <= buttonY + BUTTON_HEIGHT) { selected = i; break; } } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } return selected; } /************************** * 游戏说明界面 * @param key 当前选择的传统文化索引 **************************/ void Information(int key) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置文本样式 settextcolor(RGB(0, 0, 0)); setbkmode(TRANSPARENT); settextstyle(40, 20, "华文宋体"); // 游戏说明文本 const char instructions[] = "这是一个传统文化主题的数字拼图游戏。\n" "目标是通过移动拼图块,使所有数字按顺序排列,右下角为空白格。\n" "每个数字对应一种中国传统文化元素:\n" "1: 京剧脸谱\n2: 中国结\n3: 剪纸\n" "4: 书法\n5: 国画\n6: 陶瓷\n" "7: 刺绣\n8: 皮影\n9: 灯笼\n\n" "操作方式:\n" "方向键 - 移动拼图\n" "ESC - 退出游戏\n" "L - 查看传统文化元素列表\n" "按任意键返回主菜单..."; // 计算文本位置使其居中 int startX = 50, startY = 100; int lineHeight = 50; // 分行输出文本 char* context = NULL; char* line = strtok_s((char*)instructions, "\n", &context); while (line != NULL) { outtextxy(startX, startY, line); startY += lineHeight; line = strtok_s(NULL, "\n", &context); } // 等待按键 _getch(); Menu(); } /************************** * 初始化拼图游戏 * @param game 游戏结构体指针 * @param size 拼图尺寸 * @param picIndex 选择的图片索引 **************************/ void InitPuzzle(struct PuzzleGame* game, int size, int picIndex) { game->size = size; game->picIndex = picIndex; game->moves = 0; // 初始化拼图数据 int num = 1; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { game->board[i][j] = (i == size - 1 && j == size - 1) ? 0 : num++; } } game->emptyRow = size - 1; game->emptyCol = size - 1; // 随机打乱拼图 for (int i = 0; i < size * size * 100; i++) { int dir = rand() % 4; int newRow = game->emptyRow, newCol = game->emptyCol; switch (dir) { case 0: newRow--; break; // 上 case 1: newRow++; break; // 下 case 2: newCol--; break; // 左 case 3: newCol++; break; // 右 } // 检查移动是否有效 if (newRow >= 0 && newRow < size && newCol >= 0 && newCol < size) { // 交换空白格和目标格 game->board[game->emptyRow][game->emptyCol] = game->board[newRow][newCol]; game->board[newRow][newCol] = 0; game->emptyRow = newRow; game->emptyCol = newCol; } } } /************************** * 检查是否胜利 * @param game 游戏结构体指针 * @return 是否完成拼图 **************************/ bool CheckWin(struct PuzzleGame* game) { int num = 1; for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { // 检查右下角是否为空白格 if (i == game->size - 1 && j == game->size - 1) { if (game->board[i][j] != 0) return false; } else { // 检查其他格子是否按顺序排列 if (game->board[i][j] != num++) return false; } } } return true; } /************************** * 移动拼图块 * @param game 游戏结构体指针 * @param direction 移动方向 * @return 移动是否成功 **************************/ bool MoveTile(struct PuzzleGame* game, int direction) { int newRow = game->emptyRow; int newCol = game->emptyCol; // 计算目标位置 switch (direction) { case 0: newRow--; break; // 上 case 1: newRow++; break; // 下 case 2: newCol--; break; // 左 case 3: newCol++; break; // 右 default: return false; } // 检查边界 if (newRow < 0 || newRow >= game->size || newCol < 0 || newCol >= game->size) { return false; } // 交换空白格和目标格 game->board[game->emptyRow][game->emptyCol] = game->board[newRow][newCol]; game->board[newRow][newCol] = 0; game->emptyRow = newRow; game->emptyCol = newCol; game->moves++; return true; } /************************** * 绘制拼图界面 * @param game 游戏结构体指针 **************************/ void DrawPuzzle(struct PuzzleGame* game) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置文本样式 settextstyle(30, 15, "华文宋体"); setbkmode(TRANSPARENT); settextcolor(WHITE); // 显示标题和步数 outtextxy(50, 20, "传统文化拼图游戏"); char movesText[50]; sprintf(movesText, "步数: %d", game->moves); outtextxy(50, 60, movesText); // 计算拼图块大小和位置 int blockSize = 80; int startX = (WINDOW_WIDTH - game->size * blockSize) / 2; int startY = 150; // 加载选中的图片 char filename[100]; sprintf(filename, "picture/%d.jpg", game->picIndex + 1); IMAGE img; loadimage(&img, filename); // 检查图片是否成功加载 if (img.getwidth() <= 0 || img.getheight() <= 0) { // 如果图片加载失败,使用默认数字块 for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { int x = startX + j * blockSize; int y = startY + i * blockSize; if (game->board[i][j] == 0) { // 绘制空白格 setfillcolor(RGB(200, 200, 200)); fillrectangle(x, y, x + blockSize, y + blockSize); settextcolor(BLACK); outtextxy(x + blockSize / 2 - 10, y + blockSize / 2 - 10, "●"); } else { // 绘制拼图块 setfillcolor(RGB(231, 250, 235)); fillrectangle(x, y, x + blockSize, y + blockSize); // 显示数字 char numText[10]; sprintf(numText, "%d", game->board[i][j]); settextcolor(BLACK); outtextxy(x + blockSize / 2 - 10, y + blockSize / 2 - 10, numText); } // 绘制边框 setlinecolor(BLACK); rectangle(x, y, x + blockSize, y + blockSize); } } } else { // 图片加载成功,切割图片并显示 int imgWidth = img.getwidth(); int imgHeight = img.getheight(); for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { int x = startX + j * blockSize; int y = startY + i * blockSize; if (game->board[i][j] == 0) { // 绘制空白格 setfillcolor(RGB(200, 200, 200)); fillrectangle(x, y, x + blockSize, y + blockSize); } else { // 计算当前拼图块在原图中的位置 int pieceIndex = game->board[i][j] - 1; // 0到size*size-1 int pieceRow = pieceIndex / game->size; int pieceCol = pieceIndex % game->size; // 计算在原图中的坐标和大小 int srcX = pieceCol * (imgWidth / game->size); int srcY = pieceRow * (imgHeight / game->size); int pieceWidth = imgWidth / game->size; int pieceHeight = imgHeight / game->size; // 切割并显示拼图块 IMAGE piece; loadimage(&piece, NULL, pieceWidth, pieceHeight); // 设置原图为当前绘图设备 SetWorkingImage(&img); // 从原图中切割指定区域 getimage(&piece, srcX, srcY, pieceWidth, pieceHeight); // 恢复默认绘图设备 SetWorkingImage(NULL); putimage(x, y, &piece); } // 绘制边框 setlinecolor(BLACK); rectangle(x, y, x + blockSize, y + blockSize); } } } // 显示操作提示 settextstyle(20, 10, "华文宋体"); outtextxy(50, WINDOW_HEIGHT - 60, "方向键: 移动拼图 ESC: 退出游戏 L: 查看元素列表"); } /************************** * 显示胜利界面 * @param game 游戏结构体指针 **************************/ void ShowWinScreen(struct PuzzleGame* game) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置胜利信息样式 settextstyle(50, 25, "华文行楷"); setbkmode(TRANSPARENT); settextcolor(RGB(255, 215, 0)); // 金色 // 显示胜利信息 outtextxy(WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 - 100, "恭喜你完成了拼图!"); // 显示步数 char movesText[50]; sprintf(movesText, "总步数: %d", game->moves); outtextxy(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2, movesText); // 显示返回提示 settextstyle(30, 15, "华文宋体"); outtextxy(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 + 100, "按任意键返回主菜单"); // 等待按键 _getch(); } /************************** * 拼图游戏主循环 * @param degree 难度级别 * @param picIndex 选择的图片索引 **************************/ void PuzzlePage(int degree, int picIndex) { struct PuzzleGame game; InitPuzzle(&game, degree, picIndex); while (true) { DrawPuzzle(&game); // 检查胜利条件 if (CheckWin(&game)) { ShowWinScreen(&game); break; } // 处理键盘输入 if (peekmessage(&msg, EX_KEY)) { if (msg.message == WM_KEYDOWN) { switch (msg.vkcode) { case VK_UP: MoveTile(&game, 0); break; case VK_DOWN: MoveTile(&game, 1); break; case VK_LEFT: MoveTile(&game, 2); break; case VK_RIGHT: MoveTile(&game, 3); break; case 'L': // 显示传统文化元素列表 Information(picIndex); break; case VK_ESCAPE: return; // 返回上级菜单 } } } Sleep(50); // 控制刷新率 } } /************************** * 主菜单界面 **************************/ void Menu() { // 初始化图形窗口 initgraph(WINDOW_WIDTH, WINDOW_HEIGHT); // 设置背景和音乐 GetImage("picture/bk.jpg", 0, 0); PlaySound("ragtime.wav", NULL, SND_ALIAS | SND_FILENAME | SND_ASYNC | SND_ALIAS); // 设置标题样式 settextcolor(RGB(39, 220, 220)); setbkmode(TRANSPARENT); settextstyle(100, 30, "华文行楷"); outtextxy(150, 50, "传统文化拼图游戏"); // 绘制菜单按钮 const char* word[] = { "开始游戏","游戏说明","退出" }; for (int i = 0; i < 3; i++) { Button(100, i * 150, word[i]); } // 处理菜单选择 while (1) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查开始游戏按钮 if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 0 && msg.y <= BUTTON_Y + 0 + BUTTON_HEIGHT) { int degree = ModeSelection(); int picIndex = Selectpicture(); PuzzlePage(degree, picIndex); return; } // 检查游戏说明按钮 else if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 150 && msg.y <= BUTTON_Y + 150 + BUTTON_HEIGHT) { Information(key); return; } // 检查退出按钮 else if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 300 && msg.y <= BUTTON_Y + 300 + BUTTON_HEIGHT) { closegraph(); exit(0); } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } } 为什么点击游戏说明会直接跳掉
最新发布
06-08
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值